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

相关推荐
m0_5287238110 分钟前
HTML中,title和h1标签的区别是什么?
前端·html
Dark_programmer10 分钟前
html - - - - - modal弹窗出现时,页面怎么能限制滚动
前端·html
GDAL16 分钟前
HTML Canvas clip 深入全面讲解
前端·javascript·canvas
禾苗种树17 分钟前
在 Vue 3 中使用 ECharts 制作多 Y 轴折线图时,若希望 **Y 轴颜色自动匹配折线颜色**且无需手动干预,可以通过以下步骤实现:
前端·vue.js·echarts
贵州数擎科技有限公司38 分钟前
使用 Three.js 实现流光特效
前端·webgl
JustHappy40 分钟前
「我们一起做组件库🌻」做个面包屑🥖,Vue的依赖注入实战💉(VersakitUI开发实录)
前端·javascript·github
拉不动的猪1 小时前
刷刷题16
前端·javascript·面试
祈澈菇凉2 小时前
如何结合使用thread-loader和cache-loader以获得最佳效果?
前端
垣宇2 小时前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
java1234_小锋2 小时前
一周学会Flask3 Python Web开发-客户端状态信息Cookie以及加密
前端·python·flask·flask3