懂 Vue、React 就懂 Flutter 交互事件 - 上篇

说到事件就不得不聊到 Flutter 中的核心概念 Stateful Widget 有状态组件,它代表了可以在其生命周期中改变状态。什么叫做状态发生改变呢? 比如有用户的交互行为,点击或者滑动,再比如从服务端接收到了新的数据。当这些状态发生改变时,Flutter 就会调用 build 方法来重新构建这些有状态组件,来确保小组件的状态和 UI 是同步的。

举个🌰,我们写一个 Switcher

先定义一个有状态小组件,通过继承 StatefulWidget, 然后 override createState 方法来创建一个状态

dart 复制代码
class MySwitch extends StatefulWidget { 
    @override 
    _MySwitchState createState() => _MySwitchState(); 
}

接下来就要写我们本节的重点:事件交互!也就是当用户点击 Switch 切换状态,UI 也要跟着变

来实现这个状态,以及状态相关的 UI

dart 复制代码
class _MySwitchState extends State<MySwitch> {
  bool _switchValue = false;

  @override
  Widget build(BuildContext context) {
    return Switch(
      value: _switchValue,
      onChanged: (value) {
        setState(() {
          _switchValue = value;
        });
      },
    );
  }
}

我们用了一个 Switch 组件,当用户点击 Switch 时会触发事件 onChanged, 然后我们在这个函数中通过调用setState()方法来改变_switchValue的值。setState()方法会触发build()方法的重新执行,从而更新Switch的UI。也就是说 setState 用于通知 Flutter 框架,有状态的Widget(StatefulWidget)的状态发生了变化,这样Flutter就会更新用户界面。

需要注意的是,不是所有的数据的改变都需要调用setState()。只有当数据的改变会导致UI的更新时,我们才需要调用setState()。通常这些状态都会被定义在 State 类中,做 UI 一定要做好状态管理,否则根本分不清UI在什么情况下变,什么情况下不变

那稍微来总结一下如何构建一个有状态组件,只需要2步

  1. 继承 StatefulWidget,通过override createState 创建一个状态
  2. 继承 State, 给 Flutter 提供个 build 方法, 为了当我们状态在 setState 变化时,Flutter 通知重新构建组件

父组件管理状态

刚才这个例子中,我们是自己管理 Switch 的状态, 但是在复杂应用中,我们的 Switch 状态可能会被其他组件所依赖,我们怎么传递给其他组件呢? 需要通过一个"中介",这个中介就是父组件,这就类似于租房,我们是房东,我们需要将房子钥匙委托给中介,让它来保管和带租客看房。

让我们改造一下,创建一个允许用户切换夜间模式的 Switcher

父组件里面设置一个 _NeightValue 状态,子组件里面用 Switch 来修改这个值,当 _NeightValue 变化,我们就将主题设置成夜晚模式。 这样的话这个子组件改成一个 Stateless 组件就行,也就是 React 中的受控组件。

先来写父组件, 我们用到 MaterialApp 中提供的 theme 属性来实现暗黑主题

dart 复制代码
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _isNightMode = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: _isNightMode ? ThemeData.dark() : ThemeData.light(),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Night Mode Switch Example'),
        ),
        body: Center(
          child: MySwitch(
            switchValue: _isNightMode,
            onChanged: (value) {
              setState(() {
                _isNightMode = value;
              });
            },
          ),
        ),
      ),
    );
  }
}

Line 18 我们调用子组件,这里注意我们传递给他 2 个参数, 这 2 个参数在子组件里我们要声明一下

dart 复制代码
class MySwitch extends StatelessWidget {
  final bool switchValue;
  final ValueChanged<bool> onChanged;

  MySwitch({required this.switchValue, required this.onChanged});

  @override
  Widget build(BuildContext context) {
    return Switch(
      value: switchValue,
      onChanged: onChanged,
    );
  }
}

在Flutter中,是否在父组件中管理状态取决于具体的使用场景。对于一些简单的情况,直接在Widget内部管理状态就可以了。但是,当状态变得复杂,特别是多个组件需要共享状态时,通常推荐在父组件或者提供了状态管理功能的组件(如Provider)中管理状态, 可以参考本系列另一篇关于状态管理的文章。这样可以更好地组织代码,使状态管理变得更易于理解和维护。

手势

有些组件是自带事件的,有些组件需要我们自己绑定事件,比如 Text 组件,我们如果实现点击这个 Text 更改 Text 的文本呢? 最简单的方式就是使用手势 GestureDetector, 只需要将它包裹在你希望添加手势识别的部件周围,然后传入相应的回调函数。

dart 复制代码
@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: Text('Flutter Night Mode Switch Example'),
      ),
      body: Center(
        child: GestureDetector(
          onTap: (){
            print('something happend');
          },
          child: Text("Click me"),
        )
      ),
    ),
  );
}

GestureDetector是Flutter中一个非常关键的部件,它可以对触摸事件进行识别。你可以用它来识别各种类型的手势,包括点击、双击、长按、拖动等等。

只是 print 一下很无聊,我们来使用 SnackBar 组件来做一个点击反馈, SnackBar 会在屏幕的底部显示一条消息,并在一段时间后自动消失。

dart 复制代码
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Snackbar 示例'),
        ),
        body: Center(
          child: Builder(
            builder: (context) => GestureDetector(
              child: Text('Click'),
              onTap: () {
                final snackBar = SnackBar(
                  content: Text('click!'),
                );
                ScaffoldMessenger.of(context).showSnackBar(snackBar);
              },
            ),
          ),
        ),
      ),
    );
  }
}

Line 17 ScaffoldMessenger 是 Flutter 中的一个类,它提供了一个框架用于显示SnackBar。ScaffoldMessenger 可以包含多个 Scaffold 的上下文,它可以用来控制在这些 Scaffold 中显示SnackBar 通过调用ScaffoldMessenger的showSnackBar方法。

除了 GestureDetector 外,还可以通过 InkWell 添加事件,使用方法一模一样,InkWell 和GestureDetector 都是 Flutter 中用于处理触摸事件的 Widget,但它们之间有一些关键区别。GestureDetector 更为基础,它可以识别各种手势,如点击、双击、长按、拖动等,并且它不包含任何视觉表示。换句话说,GestureDetector 本身不会改变显示的视图,它只是识别和响应手势。

相比之下,InkWell 不仅可以识别触摸事件,还可以在用户触摸屏幕时显示水波纹效果。这提供了一种视觉反馈,让用户知道他们的操作已经被接收。因此用哪个取决于你是否需要在用户触摸屏幕时提供视觉反馈。

相关推荐
电商API_180079052475 分钟前
电商商品价格监控工具淘宝京东商品价格抓取API项目实操分享
java·大数据·开发语言·前端·数据挖掘
sxmas16 分钟前
用状态机与DAG建模四阶段业务流程:以MIND模型为例
前端
倔强的石头1061 小时前
【Linux指南】动静态库系列(七):符号表与重定位:链接器如何把函数调用接起来
linux·前端·javascript
Android-Flutter2 小时前
flutter GetX 详解
android·flutter
a1117762 小时前
3D 脑图谱 / Bilingual 3D Brain Atlas 项目
前端
T1mzhou2 小时前
ARM64 Linux 6.10内核启动流程2-rootfs 怎么挂上
linux·服务器·前端
小小尚@3 小时前
WebGIS/ECharts 地图开发|MapLand 在线行政区划边界一键下载 GeoJSON 工具
前端·javascript·echarts
默_笙4 小时前
🚍 一条 Todo 的奇幻漂流:TypeScript 全栈类型安全的"护照检查"
前端·javascript
计算机魔术师4 小时前
NVIDIA 以 129.3 亿美元收购 Hugging Face
前端
平头哥技术团队4 小时前
Day 01 | 用 HTML 写第一个网页:双击就能在浏览器打开
前端