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

相关推荐
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
正小安6 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch8 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光8 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   8 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   8 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web8 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery