Credit: Duy Nguyen Ngoc

Giới thiệu

Flutter Hooks, đối với anh em mobile thì khái niệm này khá là xa lạ. Tuy nhiên, nếu ai đã từng code ReactJs thì sẽ thấy khái niệm này khá là quen thuộc. Và đúng là như vậy, Flutter Hooks được lấy cảm hứng từ React Hooks. Dựa theo React Hooks và tác phẩm kinh điển Making sense of React Hooks của Dan Abramov các nhà phát triển tại Dash Overflow đã quyết định đưa Hooks vào Flutter. Trong Flutter, khái niệm Widget nó có các tính năng khá tương tự như Component trong React và nhiều vòng đời trong thành phần React có trong Widget của Flutter. Trên trang Github của Flutter Hooks có nêu rõ mục đích nó:

Hooks are a new kind of object that manage the life-cycle of a Widget. They exist for one reason: increase the code-sharing between widgets by removing duplicates.

Tạm dịch, Hooks là một loại đối tượng mới quản lý vòng đời của Widget. Hook được được tạo ra vì một lý do: tăng khả năng chia sẻ code giữa các widget con bằng cách loại bỏ các mã trùng lặp.

 

Tóm lại, Hooks là một cách mới để quản lý vòng đời và trạng thái của widget trong Flutter. Trong các nội dung tiếp theo, chúng ta tìm hiểu cách cài đặt và sẽ khám phá một số loại Hooks khác nhau cũng như cách sử dụng chúng trong Flutter.

Cài đặt flutter_hooksthư viện

Để sử dụng Flutter Hook từ flutter_hooksthư viện, chúng ta phải cài đặt nó bằng cách chạy lệnh sau trong terminal bên trong dự án Flutter:

flutter pub add flutter_hooks

Ngoài ra, chúng ta có thể thêm flutter_hooksvào dependenciesphần trong tệp:pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  flutter_hooks:

Sau khi lưu lại, Flutter sẽ cài đặt phần phụ thuộc. Tiếp theo, import flutter_hooks vào file để có thể sử dụng:

import 'package:flutter_hooks/flutter_hooks.dart';

Vậy là có thể chiến!

 

Thư viện flutter_hooks cung cấp rất nhiều hooks:

  • useEffect
  • useState
  • useMemoized
  • useRef
  • useCallback
  • useContext
  • useValueChanged
  • ...

Trong bài viết này, chúng ta sẽ tập trung vào 2 Hooks chính:

  • Hook useStatequản lý các trạng thái cục bộ trong ứng dụng
  • Hook useEffecttìm nạp dữ liệu từ máy chủ và đặt tìm nạp thành trạng thái cục bộ

useState

Sau khi tạo một project flutter chúng ta sẽ có một ví dụ sử dụng StatefulWidget để tạo một counter example. Trong ứng dụng này, chúng ta có thể thay đổi giá trị bộ đếm bằng cách nhấn vào nút “+”. Mỗi lần nhấn sẽ thay đổi giao diện người dùng và rebuild widget để cập nhật giá trị counter. Chúng ta sẽ lưu trữ biến counter và hiển thị nó trong giao diện người dùng

 

Mình tách nó ra một file riêng và đặt tên là counter_without_hooks.dart, đoạn mã như sau:

 

import 'package:flutter/material.dart';

class CounterWithoutHooks extends StatefulWidget {
  const CounterWithoutHooks({Key? key}) : super(key: key);

  @override
  State<CounterWithoutHooks> createState() => _CounterWithoutHooksState();
}

class _CounterWithoutHooksState extends State<CounterWithoutHooks> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Counter - Stateful Widget"),
      ),
      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.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Sử dụng StatefulWidget duy trì trạng thái cục bộ trong widget đôi khi phức tạp. Chúng ta cũng phải giới thiệu một lớp khác 

extends class State, tạo hai lớp cho một tệp StatefulWidget.
Và mình để ý rằng chúng ta tạo một lớp này trong khi chả bao giờ thấy dùng cả:
class CounterWithoutHooks extends StatefulWidget {
  const CounterWithoutHooks({Key? key}) : super(key: key);

  @override
  State<CounterWithoutHooks> createState() => _CounterWithoutHooksState();
}

Tuy nhiên, với Hook, chúng ta sẽ chỉ sử dụng một lớp để duy trì code của mình, giúp việc bảo trì dễ dàng hơn so với StatefulWidget.

Dưới đây là code sử dụng Hook:

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

class CounterWithHooks extends HookWidget {
  const CounterWithHooks({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final counter = useState(0);
    return Scaffold(
      appBar: AppBar(
        title: Text("Counter - Hook Widget"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counter.value}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => counter.value++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
      // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Ví dụ về Hook ngắn hơn so với code ban đầu. Tuy nhiên, trước khi sử dụng Flutter Hooks trong một widget, widget đó phải được 

extends HookWidget do flutter_hooksthư viện cung cấp.
Trong ví dụ ở trên, giá trị counter ban đầu được đặt thành 0, initialData sẽ được đặt khi khởi tạo hook. Khi counter tiếp tục cập nhật, ValueNotifier.value sẽ thay đổi và widget sẽ được rebuild lại.

useEffect

Vì không kế thừa từ class StatefulWidget chúng ta sẽ không có ham initState vậy đối với các action cần chạy ngay khi widget được khởi tạo chúng ta sẽ sử dụng hook useEffect().

 

Hook useEffecttrong Flutter giống như useEffectHook của React. Hook lấy hàm gọi lại làm tham số và chạy các tác dụng phụ trong một widget:

  • Subscription một stream
  • Tạo timer
  • Mở một WebSocket connection
  • Gọi api http

 

useEffect( () {
    // side effects code.
});

Hàm callback này phải trả về một hàm và được gọi khi widget disposed. Sau đó, chúng ta thực hiện các công việc dọn dẹp bao gồm:

  • Huỷ bỏ subscribe của một stream
  • Clear các timeouts
  • Huỷ bỏ active HTTP connections
  • Huỷ bỏ WebSockets connections
useEffect( () {
    // side effects code.
         return () {
        // clean up code
    }
});

Hàm được return trong useEffect sẽ được gọi đồng bộ.

 

Tiếp đến Hooks này cũng có một đối số thứ hai tùy chọn có tên keys. Đối keyssố là một danh sách các giá trị xác định xem hàm gọi lại trong useEffectHook có được gọi hay không.

 

useEffectso sánh các giá trị hiện tại của keysso với các giá trị trước đó của nó. Nếu các giá trị khác nhau, useEffect sẽ chạy lại hàm. Nếu chỉ có một giá trị trong keysgiữ nguyên, hàm gọi lại không được gọi:

 

useEffect( () {
    // side effects code here.
    return () {
        // clean up code
    }
}, [keys]);

 

Tóm tắt lại:

  • Những công việc side effect sẽ được viết ở hàm callback trong useEffect
  • Những công việc clean up sẽ được viết tại hàm được return trong hàm callback và sẽ được thực hiện khi widget disposed

Tổng kết

Flutter Hooks là một thư viện cho phép quản lý vòng đời của các widget trong Flutter. Flutter widget có các tính năng tương tự như React components. Vì vậy, Flutter Hooks được lấy cảm hứng từ React Hooks và được Dash Overflow phát triển để giúp tăng khả năng chia sẻ mã giữa các widget và giảm số lượng mã lặp lại.

 

Flutter_hooks cung cấp một cách quản lý vòng đời mạnh mẽ và hiệu quả để giảm mã lặp lại. Thư viện cung cấp các Hooks tích hợp sẵn như: useEffect, useState, useMemoized, useRef, useCallback, useContext và useValueChanged. Trong bài viết này, chúng ta sẽ tập trung vào ba Hooks chính: useState, useEffect và useMemoized. 

 

Để sử dụng Flutter Hooks từ thư viện flutter_hooks, chúng ta cần cài đặt nó bằng lệnh "flutter pub add flutter_hooks". Sau đó, chúng ta import flutter_hooks vào file mã nguồn của mình để sử dụng các Hook.

 

Hook useState giúp tạo và quản lý trạng thái cục bộ trong ứng dụng. Nó được sử dụng để tạo ra trạng thái khởi tạo của widget và có thể thay đổi trong quá trình thực thi. Hook này nhận trạng thái cục bộ của widget làm tham số đầu vào.

 

Hook useEffect được sử dụng để lấy dữ liệu từ máy chủ và đặt fetch cho trạng thái cục bộ.

 

Việc sử dụng Hooks trong Flutter giúp cho việc quản lý trạng thái của widget dễ dàng hơn và giúp tiết kiệm thời gian và công sức trong việc phát triển ứng dụng. Đặc biết cách tiếp cận này rất dễ cho anh em đã code ReactJS làm quen với cô nàng Flutter