【Flutter 工程】003-钩子函数:Flutter Hooks

news/2024/11/20 2:25:46/

【Flutter 工程】003-钩子函数:Flutter Hooks

文章目录

  • 【Flutter 工程】003-钩子函数:Flutter Hooks
  • 一、概述
    • 1、前言
    • 2、Flutter Hooks 概述
  • 二、`useState` 基本使用
    • 0、计数器官方 demo
    • 1、安装 flutter_hooks
    • 2、代码改造
    • 3、运行结果
    • 4、神奇的事情
  • 三、使用 `HookBuilder` 实现更小范围的组件更新
    • 1、简单分析
    • 2、使用 `HookBuilder` 实现更小范围的组件更新
      • 代码改造
      • 运行结果
      • 控制台

一、概述

1、前言

Hooks 是 React 框架中引入的一项特性,用来分离状态逻辑和视图逻辑。如今,这个概念已经不仅限于 React,其他前端框架也在学习和借鉴。在 Flutter 开发中,业务逻辑和视图逻辑的耦合一直是一个比较突出的痛点,这也是各大前端框架常遇到的一个共性难题。为了解决这个问题,前端社区提出了许多方案,如MVP、MVVM、React 的Mixin、高阶组件(HOC),以及Hooks。在Flutter中,开发者可能对Mixin比较熟悉。但是,Mixin的应用也存在一定的局限性:

  • Mixin 之间可能互相依赖,导致依赖关系混乱;
  • Mixin 之间可能产生冲突,难以识别和解决;
  • Mixin的可复用性有限,不利于组件解耦。

基于此,本文将介绍 Hooks 的概念和应用,看是否能够避免 Mixin 的这些限制,实现更好的状态逻辑和视图逻辑的解耦。Hooks 为我们提供了一种无需修改组件结构的方式来复用状态逻辑。我们可以通过 Hooks 将复杂的状态逻辑抽离出来,这有助于提高组件的内聚性,实现高度可复用的状态逻辑。

2、Flutter Hooks 概述

Flutter Hooks是一个用于 Flutter 应用程序的第三方包,它提供了一种优雅且方便的方式来管理 Flutter 小部件的状态和生命周期

在Flutter中,通常使用 StatefulWidget 来管理具有可变状态的小部件。然而,使用 StatefulWidget 可能会导致代码冗长,因为需要创建一个单独的State类来管理状态,并且需要在小部件和状态之间进行额外的通信

Flutter Hooks通过使用钩子(hooks)的概念,提供了一种更简洁的方式来管理小部件的状态。钩子是一些函数,可以在小部件函数内部调用,它们提供了一种轻量级的状态管理机制。

使用Flutter Hooks,您可以在无需创建 StatefulWidget 的情况下管理状态。它提供了一些常用的钩子函数,例如useState(用于管理状态)、useEffect(用于处理生命周期)、useMemo(用于记忆计算结果)和useCallback(用于记忆回调函数)。这些钩子函数使得管理小部件的状态和副作用变得更加简单和直观。

除了内置的钩子函数,Flutter Hooks还提供了自定义钩子函数的能力,以便您可以根据应用程序的需求创建自己的钩子。

总的来说,Flutter Hooks是一个强大而灵活的库,可以帮助开发者更好地组织和管理Flutter应用程序中的小部件状态和生命周期。它可以提高代码的可读性和可维护性,并且减少了使用传统的 StatefulWidget 时的样板代码量。

二、useState 基本使用

0、计数器官方 demo

去掉了注释!

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),useMaterial3: true,),home: const MyHomePage(title: 'Flutter Demo Home Page'),);}
}class MyHomePage extends StatefulWidget {const MyHomePage({super.key, required this.title});final String title;State<MyHomePage> createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {int _counter = 0;void _incrementCounter() {setState(() {_counter++;});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(widget.title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:',),Text('$_counter',style: Theme.of(context).textTheme.headlineMedium,),],),),floatingActionButton: FloatingActionButton(onPressed: _incrementCounter,tooltip: 'Increment',child: const Icon(Icons.add),),);}
}

1、安装 flutter_hooks

flutter pub add flutter_hooks

2、代码改造

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),useMaterial3: true,),home: const MyHomePage(title: 'Flutter Demo Home Page'),);}
}// 关注:继承自 HookWidget
class MyHomePage extends HookWidget {const MyHomePage({super.key, required this.title});final String title;Widget build(BuildContext context) {// 关注:使用 useStatefinal counter = useState(0);print("执行时间:" + DateTime.now().toString() + "  counter.value = " + counter.value.toString());return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(title),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:',),// 关注:使用 counter.valueText('${counter.value}',style: Theme.of(context).textTheme.headlineMedium,),],),),floatingActionButton: FloatingActionButton(// 关注:使用 counter.value ++onPressed: () => counter.value++,tooltip: 'Increment',child: const Icon(Icons.add),),);}
}

3、运行结果

image-20230526130958068

4、神奇的事情

hook 的实现原理这里暂不探讨,可参考文章:https://juejin.cn/post/7220295071060541495

  • counter 的值更新了,也重新执行了 build 方法,但是 counter 变量的值并没有被重新初始化,而是实现了复用!

    Syncing files to device Windows...
    flutter: 执行时间:2023-05-26 13:14:56.030115  counter.value = 14
    Reloaded 1 of 668 libraries in 168ms (compile: 23 ms, reload: 62 ms, reassemble: 49 ms).
    flutter: 执行时间:2023-05-26 13:15:12.404470  counter.value = 15
    
  • 没有 State 代码,HookWidget 是继承 StatelessWidget

    abstract class HookWidget extends StatelessWidget {/// Initializes [key] for subclasses.const HookWidget({Key? key}) : super(key: key);_StatelessHookElement createElement() => _StatelessHookElement(this);
    }
    

三、使用 HookBuilder 实现更小范围的组件更新

1、简单分析

上述示例中,发生变化的仅仅是 count 的值,需要更新的也只是一个 Text 组件的文本,但由于 count 的值的更新却导致整个页面的重建,这是不合理的!

传统的解决方案是将 Text 单独封装,但这徒增了很多代码!flutter_hooks 提供了 HookBuilder 来实现这个需求!

2、使用 HookBuilder 实现更小范围的组件更新

代码改造

这里做一个简单示例来展示基本用法,不纠结极客风格的代码!

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),useMaterial3: true,),home: const MyHomePage(title: 'Flutter Demo Home Page'),);}
}// 关注:继承自 HookWidget
class MyHomePage extends HookWidget {const MyHomePage({super.key, required this.title});final String title;Widget build(BuildContext context) {print("外层 build 执行时间:" + DateTime.now().toString());return Scaffold(appBar: AppBar(backgroundColor: Theme.of(context).colorScheme.inversePrimary,title: Text(title),),// 关注:使用 HookBuilderbody: HookBuilder(builder: (context) {print("内层 build 执行时间:" + DateTime.now().toString());final counter = useState(0);return Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[const Text('You have pushed the button this many times:',),// 关注:使用 counter.valueText('${counter.value}',style: Theme.of(context).textTheme.headlineMedium,),FloatingActionButton(// 关注:使用 counter.value ++onPressed: () => counter.value++,tooltip: 'Increment',child: const Icon(Icons.add),),],),);},),);}
}

运行结果

image-20230526134057742

控制台

Syncing files to device Windows...
flutter: 外层 build 执行时间:2023-05-26 13:40:46.152943
flutter: 内层 build 执行时间:2023-05-26 13:40:46.182019
flutter: 内层 build 执行时间:2023-05-26 13:40:49.101309
flutter: 内层 build 执行时间:2023-05-26 13:40:49.297099
flutter: 内层 build 执行时间:2023-05-26 13:40:49.644307

http://www.ppmy.cn/news/89127.html

相关文章

HTTP中 Connection: keep-Alive与TCP中中keepalive有什么区别?

有小伙伴不明白keep-Alive和keepalive有什么区别&#xff1f;今天写这篇文章详细讲清楚&#xff01; HTTP是请求响应模型也就是客户端发起了请求&#xff0c;服务端才会返回响应&#xff0c;一来一回。 由于 HTTP 是基于 TCP 传输协议实现的&#xff0c;客户端与服务端要进行 H…

RESTful API介绍

RESTful API&#xff08;Representational State Transfer&#xff09;是一种设计 Web 应用程序的架构风格&#xff0c;它使用 HTTP 请求来进行数据传输和交互。 RESTful API 的核心思想是将资源&#xff08;比如用户、订单、产品&#xff09;作为 Web 上的唯一标识&#xff0…

公文写作素材:为人处世类过渡句50例

1.身处逆境&#xff0c;敢于亮剑&#xff0c;坚毅前行&#xff0c;方能逆势突围&#xff1b;面对困难&#xff0c;坚定信心&#xff0c;敢拼敢闯&#xff0c;定能笑到最后。 2.没有海纳百川的胸怀&#xff0c;怎能容得下不同性格的人&#xff1b;没有从善如流的雅量&#xff0…

【人工智能概论】 数据预处理——MaxMin归一化、Z-Score异常判断、经验异常判断

【人工智能概论】 数据预处理——MaxMin归一化、Z-Score异常判断、经验异常判断 文章目录 【人工智能概论】 数据预处理——MaxMin归一化、Z-Score异常判断、经验异常判断一. MaxMin归一化1.1 数据标准化的目的与好处1.2 归一化公式1.3 归一化代码 二. Z-Score2.1 Z-Score 原理…

CCSC认证报考攻略

CCSC认证介绍 国家计算机网络应急技术处理协调中心(CNCERT)是中央网信办公室直属事业单位&#xff0c;是“网络安全培训资助计划”发起人和技术指导单位之一。CNCERT拥有先进的实验平台、顶尖的专家团队和丰富的人才培训经验&#xff0c;熟悉政府部门、行业协会和企业事业单位…

C++ QT QDBus进阶用法。

以下是使用QDBus的高级用法示例代码&#xff1a; 1. 使用DBus的异步调用机制&#xff1a; #include <QCoreApplication> #include <QDebug> #include <QDBusConnection> #include <QDBusPendingCallWatcher> class MyDBusObject : public QObject …

IO流的讲解(2)

目录 文件拷贝 FileReader FileWriter 节点流和处理流 基本介绍 节点流和处理流的区别 处理流-BufferedReader和BufferedWriter 案例1 案例2 案例3 对于文件的拷贝&#xff0c;对于我们日常的使用来说&#xff0c;也就是复制粘贴的事情&#xff0c;但是我们如何在Jav…

Golang实现简单WebSocket服务

我们每天接触到各类应用&#xff0c;如社交、在线文档、直播等&#xff0c;后端都需要使用WebSocket技术提供实时通信能力。本文介绍如何使用Golang实现实时后端WebSocket服务&#xff0c;首先使用Gin框架搭建http服务&#xff0c;然后使用gorilla/websocket库实现简单后端WebS…