【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、运行结果
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),),],),);},),);}
}
运行结果
控制台
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