871 字
4 分钟
Flutter组件状态
2022-01-09

StatelessWidget#

StatelessWidget 是无状态的组件。它的内容在生命周期中保持不变,只在创建时构建一次,之后不会重新渲染,除非父组件触发重建。

  • 适用场景
    • UI 只依赖外部传入的参数。
    • UI 不会动态变化。

StatefulWidget#

StatefulWidget 是有状态的组件,它的内容可以根据用户交互或其他事件动态变化。 与 StatelessWidget 不同,它由两部分组成:

  • StatefulWidget 类:负责定义组件的不可变部分(构造函数和静态内容)。
  • State 类:负责存储和管理组件的可变状态。

Flutter 的设计通过将 StatefulWidgetState 分开,清晰地分离了静态结构和动态行为。

为何分成两个类#

  • 不可变性StatefulWidget 本身是不可变的,只有 State 才存储可变数据。这符合 Flutter 对不可变组件的设计理念。
  • 重用性:当 StatefulWidget 重建时(例如父组件触发重建),Flutter 不会重新创建对应的 State 实例,只是重新关联,这样可以保留组件的状态。

两者区别总结#

特性StatelessWidgetStatefulWidget
是否有状态无状态有状态
重新渲染依赖父组件重建状态改变时触发重建
组件数据通过构造函数传入通过 State 类存储
用途静态内容或短暂 UI动态内容,例如交互按钮、表单等
实例重用始终创建新实例State 可以在 Widget 重建时保留
性能开销较低略高(但可通过分离逻辑优化)

Flutter 将 StatefulWidget 分为两个类是为了实现以下目标:

  • 声明式 UIWidget 是不可变的,这与声明式 UI 的设计思想一致。
  • 状态与界面分离:将不可变部分(StatefulWidget)与可变部分(State)分开,简化逻辑和提升维护性。
  • 提高性能:通过保存状态避免不必要的重建开销。

这个分离让开发者的代码更具可读性,同时保证框架的高效运行

import 'package:flutter/material.dart';

void main(List<String> args) {
  runApp(MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello World'),
        ),
        body: const MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> list = [];
  @override
  Widget build(BuildContext context) {
    return ListView(children: [
      Column(
        children: list.map((value) {
          return ListTile(
            title: Text('$value'),
          );
        }).toList(),
      ),
      const SizedBox(height: 20),
      Padding(
        padding: const EdgeInsets.all(40),
        child: ElevatedButton(
            onPressed: () {
              setState(() {
                list.add('Hello World');
              });
            },
            child: const Text('增加')),
      )
    ]);
  }
}

自定义tab切换#

class MyTab extends StatefulWidget {
  const MyTab({super.key});

  @override
  State<MyTab> createState() => _MyTabState();
}

class _MyTabState extends State<MyTab> {
  int _selectedIndex = 0; // 当前选中的 tab 索引
  final tabList = ['中低风险运动', '高风险运动', '高风险运动'];
  void _onTabTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  List<Widget> getTabList() {
    final List<Widget> list = [];

    // 使用asMap来同时获取index和item
    tabList.asMap().forEach((index, item) {
      list.add(
        GestureDetector(
            onTap: () => _onTabTapped(index), // 点击时触发的事件
            behavior: HitTestBehavior.opaque,
            child: Padding(
              padding: EdgeInsets.only(top: 10),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    item, // 显示tab的名字
                    style: TextStyle(
                        color: Colors.white, // 否则为灰色
                        fontWeight: _selectedIndex == index
                            ? FontWeight.bold
                            : FontWeight.normal),
                  ),
                  Container(
                    width: ScreenAdapter.getScreenWidth() /
                        tabList.length, // 横线的宽度
                    height: 4, // 横线的高度
                    color: _selectedIndex == index
                        ? Colors.white
                        : Theme.of(context).primaryColor, // 横线的颜色
                  ),
                ],
              ),
            )),
      );
    });

    return list; // 返回列表
  }

  Widget tabbar() {
    return Container(
        width: ScreenAdapter.getScreenWidth(),
        height: ScreenAdapter.height(88),
        decoration: BoxDecoration(
          color: Theme.of(context).primaryColor,
        ),
        padding: EdgeInsets.fromLTRB(
            ScreenAdapter.height(30), 0, ScreenAdapter.height(30), 0),
        child: SingleChildScrollView(
            scrollDirection: Axis.horizontal, // 设置为横向滑动
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: getTabList().map((widget) {
                return Container(
                  width: ScreenAdapter.getScreenWidth() /
                      tabList.length, // 每个 Tab 均匀分配宽度
                  child: widget,
                );
              }).toList(),
            )));
  }

  @override
  Widget build(BuildContext context) {
    return tabbar();
  }
}
Flutter组件状态
https://www.tanghailong.com/posts/flutter/state/
作者
唐海龙
发布于
2022-01-09
许可协议
CC BY-NC-SA 4.0