likes
comments
collection
share

Flutter hooks基础类别:与组件不同生命周期交互的低级钩子

作者站长头像
站长
· 阅读数 40

一、Flutter hooks中,useMemoized详细介绍使用

useMemoized 是 Flutter Hooks 包中的一个钩子,它用于优化性能,通过记忆计算的值来避免在每次渲染时重新计算这些值。这个钩子特别适用于那些计算成本高昂的操作,比如复杂的数据转换、过滤或聚合。通过使用 useMemoized,您可以确保这些操作只有在依赖项发生变化时才会重新执行。

使用方法

useMemoized 的使用方式类似于 React 中的 useMemo。它接受两个参数:

  1. 计算函数:这是您希望执行并记忆返回结果的函数。
  2. 依赖项数组:这个数组包含了当中的项变化时,才重新计算记忆值的依赖项。

示例

下面是一个简单的使用示例:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class MemoizedExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    // 使用useMemoized记忆一个值,这个值只有在count.value变化时才重新计算
    final doubledCount = useMemoized(() => count.value * 2, [count.value]);

    return Scaffold(
      appBar: AppBar(
        title: Text('useMemoized 示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('计数器的值:${count.value}'),
            Text('记忆的值(计数器的两倍):$doubledCount'),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        tooltip: '增加',
        child: Icon(Icons.add),
      ),
    );
  }
}

在上面的例子中,我们使用 useState 钩子来创建一个状态 count。然后,我们使用 useMemoized 来记忆 count.value * 2 的结果,并将 count.value 作为依赖项。这样,只有在 count.value 发生变化时,doubledCount 的值才会重新计算。

注意事项

  • 确保依赖项数组正确无误地反映了所有应触发重新计算的条件。遗漏或错误的依赖项可能导致记忆值不正确。
  • useMemoized 并不保证永远不重新计算值。Flutter Hooks 可能会在内存压力下丢弃记忆值,强迫重新计算。因此,请不要依赖于 useMemoized 来执行副作用操作。

通过恰当使用 useMemoized,您可以在保持应用性能的同时,减少不必要的计算。

二、详细介绍Flutter hooks的useEffect

useEffect 是 Flutter Hooks 包中的一个钩子,它主要用于处理副作用(side effects),类似于 React 中的 useEffect。在 Flutter 中,副作用可以是数据获取、订阅事件或直接操作DOM。useEffect 允许您在组件的生命周期内的特定时刻执行副作用代码,同时也提供了清理(cleanup)机制。

使用方法

useEffect 接受两个参数:

  1. 副作用函数:这是一个返回 void 或返回一个可选的清理函数的函数。副作用函数执行您想要的操作,如果它返回一个函数,那么返回的函数将被用作清理机制。
  2. 依赖项数组:这个数组包含了当中的项变化时,副作用函数才会重新执行的依赖项。如果传递一个空数组 [],副作用函数只会在组件首次渲染时执行一次。如果省略这个参数,副作用函数将在每次渲染后都执行。

示例

下面是一个简单的使用示例,展示了如何在组件挂载时执行副作用,并在组件卸载时进行清理:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class EffectExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _count = useState(0);

    // 使用useEffect处理副作用
    useEffect(() {
      // 副作用代码:这里简单地打印了一条消息
      print('EffectExample 组件已挂载');

      // 清理函数
      return () {
        print('EffectExample 组件将要卸载');
      };
    }, []); // 空数组表示这个副作用仅在组件首次渲染时执行

    return Scaffold(
      appBar: AppBar(
        title: Text('useEffect 示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('您点击了 ${_count.value} 次'),
            ElevatedButton(
              onPressed: () => _count.value++,
              child: Text('点击我'),
            ),
          ],
        ),
      ),
    );
  }
}

在上面的例子中,我们使用 useState 钩子创建了一个状态 _count。然后,我们使用 useEffect 钩子来处理副作用。我们传递了一个副作用函数,它在组件首次渲染时执行,并打印了一条消息。我们还提供了一个清理函数,它在组件卸载时执行。因为我们传递了一个空的依赖项数组 [],所以这个副作用只会在组件首次渲染时执行一次。

注意事项

  • 在副作用函数中执行的操作应该是幂等的,即多次执行不会产生不同的结果,因为 Flutter Hooks 不保证副作用函数执行的精确时机。
  • 如果副作用函数返回一个清理函数,请确保清理逻辑正确无误,以避免内存泄露或其他资源管理问题。
  • 谨慎管理依赖项数组,确保正确反映了所有应触发副作用执行的条件。遗漏依赖项可能导致副作用未按预期执行,而多余的依赖项可能导致不必要的副作用执行。

通过正确使用 useEffect,您可以更好地管理 Flutter 应用中的副作用,保持代码的整洁和可维护性。

三、详细介绍Flutter hooks 的useState

useState 是 Flutter Hooks 包中提供的一个非常有用的钩子(hook),它允许您在无状态组件(即 HookWidget)中添加状态管理的能力。这是受到 React Hooks 中 useState 启发的一个功能,旨在简化和增强 Flutter 中的状态管理。

使用方法

useState 钩子允许您在组件中定义一个状态变量和一个更新该状态的函数,而不需要将组件转换为有状态组件(即 StatefulWidget)。这样,您可以享受无状态组件的简洁性和性能优势,同时具备状态管理的能力。

基本的使用方法如下:

  1. 通过调用 useState 并传递初始状态值来创建一个状态变量。
  2. useState 返回一个 StateController 对象,该对象包含当前状态值和一个用于更新状态的函数。
  3. 通过读取 StateController.value 来获取当前状态值。
  4. 通过给 StateController.value 赋值来更新状态。

示例

下面是一个简单的计数器示例,展示了如何使用 useState

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class CounterExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // 使用useState钩子创建一个状态变量
    final counter = useState(0); // counter是StateController<int>类型

    return Scaffold(
      appBar: AppBar(
        title: Text('useState 示例'),
      ),
      body: Center(
        child: Text('计数器: ${counter.value}'), // 读取状态值
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.value++, // 更新状态值
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

在这个示例中,我们使用 useState(0) 创建了一个初始值为 0 的状态变量 counter。这个变量是一个 StateController<int> 类型,它的 .value 属性可以用来读取和更新计数器的值。当用户点击浮动操作按钮时,counter.value++ 会增加计数器的值,这会触发组件的重新渲染,并显示新的计数器值。

注意事项

  • useState 应该仅在 HookWidget 中使用。
  • 状态更新(即改变 StateController.value 的值)会触发组件的重新渲染。
  • 避免在循环、条件语句或其他可能导致钩子调用顺序变化的代码中使用 useState,因为这可能会导致运行时错误。
  • 虽然 useState 提供了一种在无状态组件中管理状态的便利方式,但对于更复杂的状态管理需求,考虑使用如 ProviderRiverpodBloc 等其他状态管理方案可能更合适。

通过使用 useState,您可以轻松地在 Flutter 的无状态组件中添加和管理状态,使得组件更加灵活和功能丰富。

四、详细介绍flutter hooks的useRef

useRef 是 Flutter Hooks 包中提供的一个钩子,它被用于在组件的整个生命周期内保持对一个变量的引用。这个钩子能够帮助您存储一个可变的引用对象,而该对象不会在组件重新渲染时重置。这在某些场景下是非常有用的,比如当您需要存储一个对第三方库的引用,或者在多次渲染之间保持对某个变量的访问时。

使用方法

使用 useRef 非常简单,您只需要调用它并传递初始引用值。useRef 将返回一个 Ref 对象,该对象包含一个 value 属性,您可以通过这个属性访问或更新引用的值。

示例

下面是一个简单的示例,展示了如何使用 useRef 来存储对一个文本框 TextEditingController 的引用,并确保它在组件的整个生命周期内保持不变:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class UseRefExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // 使用useRef创建一个对TextEditingController的引用
    final textEditingController = useRef(TextEditingController());

    // 注意:在实际使用中,我们通常需要在组件卸载时释放控制器资源
    useEffect(() {
      return () => textEditingController.value.dispose();
    }, []);

    return Scaffold(
      appBar: AppBar(
        title: Text('useRef 示例'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: TextField(controller: textEditingController.value),
      ),
    );
  }
}

在这个示例中,我们使用 useRef 创建了一个对 TextEditingController 的引用,并将其存储在 textEditingController 变量中。这样,无论组件如何重新渲染,textEditingController.value 都将指向同一个 TextEditingController 实例,确保文本框的状态在组件的整个生命周期内保持一致。

此外,我们使用了 useEffect 钩子来添加一个副作用,该副作用返回一个函数,用于在组件卸载时释放 TextEditingController 资源。这是一个良好的资源管理实践,可以防止内存泄漏。

注意事项

  • useRef 返回的 Ref 对象中的 value 是可变的,您可以随时更新它,而不会触发组件的重新渲染。
  • 请确保正确管理您通过 useRef 创建的资源,尤其是那些需要手动释放的资源,例如控制器或监听器。
  • 尽管 useRef 可以用于存储任何类型的引用,但请谨慎使用,避免滥用,尤其是对状态管理。对于大多数状态管理需求,useState 或其他状态管理钩子/库可能是更合适的选择。

useRef 提供了一种在组件的生命周期内保持对变量的稳定引用的简单方式,非常适合需要跨多次渲染操作或管理外部资源的场景。

五、详细介绍Flutter hooks的useCallback

useCallback 是 Flutter Hooks 包中提供的一个钩子,它的作用是返回一个只有在依赖项改变时才会更新的回调函数。这个钩子可以帮助避免不必要的重新渲染,提高性能,特别是当回调函数作为依赖项传递给其他 Hooks 或组件时。useCallback 的行为类似于 React Hooks 中的同名钩子。

使用方法

useCallback 接受两个参数:

  1. 回调函数:您希望记忆的函数。
  2. 依赖项数组:当数组中的依赖项之一发生变化时,回调函数才会更新。如果传递空数组 [],则回调函数在组件的整个生命周期内不会更新。

示例

下面是一个使用 useCallback 的示例,展示了如何避免因父组件重新渲染导致的不必要的子组件更新:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class UseCallbackExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final count = useState(0);

    // 使用useCallback创建一个记忆化的增加计数器的函数
    final incrementCounter = useCallback(() {
      count.value++;
    }, [count.value]); // 依赖于count.value

    return Scaffold(
      appBar: AppBar(
        title: Text('useCallback 示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('计数器: ${count.value}'),
            IncrementButton(onPressed: incrementCounter), // 将回调函数传递给子组件
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class IncrementButton extends StatelessWidget {
  final VoidCallback onPressed;

  const IncrementButton({Key? key, required this.onPressed}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('IncrementButton 重新构建');
    return ElevatedButton(
      onPressed: onPressed,
      child: Text('增加'),
    );
  }
}

在这个例子中,IncrementButton 组件接受一个 onPressed 回调函数。当使用 useCallback 包装这个回调函数时,只有当 count.value 发生变化时,incrementCounter 函数才会更新。这意味着,即使父组件因为其他状态变化而重新渲染,只要 count.value 保持不变,传递给 IncrementButton 的回调函数也不会改变,从而避免了不必要的 IncrementButton 组件重建。

注意事项

  • 使用 useCallback 时,确保依赖项列表正确无误地反映了回调函数内部使用的所有状态和属性。
  • useCallback 主要用于优化性能,尤其是在将回调函数作为道具传递给子组件,或者作为其他 Hooks 的依赖项时。如果您没有遇到性能问题,过早地优化可能是不必要的。
  • 与 useMemoized 类似,useCallback 也是基于依���项数组来控制函数的更新。不同之处在于,useMemoized 用于记忆计算结果,而 useCallback 用于记忆函数本身。

useCallback 提供了一种优化组件性能的方式,通过减少不必要的函数创建和子组件重渲染,使应用运行更加高效。

六、详细介绍Flutter hooks中的useContext

useContext 是 Flutter Hooks 包中提供的一个钩子,它允许您在 HookWidget 中方便地访问 Flutter 的 BuildContext 相关的数据。这个钩子的主要作用是简化对 BuildContext 的使用,尤其是当您需要在自定义钩子中访问上下文时。useContext 返回当前 HookWidget 的 BuildContext,使您能够从中获取主题数据、路由信息、进行依赖注入等操作。

使用方法

使用 useContext 非常简单,您只需要在 HookWidget 中调用它,无需传递任何参数。useContext 将返回当前组件的 BuildContext

示例

下面是一个使用 useContext 的示例,展示了如何在 HookWidget 中获取当前主题的颜色:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class UseContextExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // 使用useContext获取当前BuildContext
    final context = useContext();
    
    // 使用BuildContext获取当前主题的颜色
    final themeColor = Theme.of(context).colorScheme.secondary;
    
    return Scaffold(
      appBar: AppBar(
        title: Text('useContext 示例'),
      ),
      body: Center(
        child: Text(
          '这是主题中的颜色',
          style: TextStyle(color: themeColor),
        ),
      ),
    );
  }
}

在这个例子中,我们在 HookWidget 中使用 useContext 钩子获取当前的 BuildContext。然后,我们使用这个上下文通过 Theme.of(context) 访问当前主题的颜色,并将其应用到文本组件的样式中。

注意事项

  • useContext 钩子只能在 HookWidget 中使用。
  • 这个钩子的主要用途是简化对 BuildContext 的访问,特别是在自定义钩子中。
  • 尽管 useContext 提供了方便的访问上下文的方式,但请注意不要过度使用上下文,以避免代码的耦合度过高。

通过使用 useContext 钩子,Flutter 开发者可以更简洁地访问 BuildContext,进而简化对各种上下文相关数据的获取。这样不仅减少了代码的冗余,也增强了代码的可读性和维护性。

七、详细介绍flutter hooks中的useValueChanged

useValueChanged 是 Flutter Hooks 包中提供的一个高级钩子,用于监听值的变化,并在值发生变化时执行一些操作。这个钩子在处理值变化时的副作用(比如状态更新、数据请求等)时非常有用。它接收一个值和一个当这个值变化时应该调用的回调函数作为参数。

使用方法

useValueChanged 接受两个参数:

  1. value:要监听的值。当这个值与上一次渲染时的值不同,将会触发回调函数的执行。
  2. onChange:一个回调函数,当监听的值发生变化时被调用。这个函数接受两个参数:新的值和旧的值。

useValueChanged 不返回任何值。

示例

下面是一个使用 useValueChanged 的示例,展示了如何监听一个计数器的值变化,并在值变化时执行一些操作:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class UseValueChangedExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final counter = useState(0);

    // 使用useValueChanged监听counter的值变化
    useValueChanged(counter.value, (int? oldValue, void _) {
      if (counter.value > oldValue!) {
        print('计数器增加了');
      } else {
        print('计数器减少了');
      }
    });

    return Scaffold(
      appBar: AppBar(
        title: Text('useValueChanged 示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('计数器: ${counter.value}'),
            ElevatedButton(
              onPressed: () => counter.value++,
              child: Text('增加'),
            ),
            ElevatedButton(
              onPressed: () => counter.value--,
              child: Text('减少'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个计数器 counter,并使用 useValueChanged 来监听 counter.value 的变化。我们的回调函数检查计数器的新值是否大于旧值,并相应地打印一条消息,说明计数器是增加了还是减少了。

注意事项

  • 使用 useValueChanged 可以简化监听值变化时的逻辑处理,但请注意避免执行过于复杂或耗时的操作,以免影响应用性能。
  • 与 React 中的 useEffect 钩子类似,useValueChanged 可以用于处理副作用,但它专门用于基于值的变化,这使得逻辑更加清晰和直接。
  • 注意 useValueChanged 的回调函数是在值发生变化后执行的,所以它不会在组件的首次渲染时被调用。

通过使用 useValueChanged 钩子,开发者可以更方便地管理和响应状态或属性的变化,从而在 Flutter 应用中实现更加灵活和响应式的用户界面

转载自:https://juejin.cn/post/7362102742762487860
评论
请登录