Chuyển đến nội dung chính

Flutter Widget Previews đã stable: hướng dẫn thực chiến với @Preview

Widget Previews ra mắt dạng thử nghiệm từ 3.35, và khá nhiều lập trình viên thử một lần, thấy chậm, rồi quay về hot reload. Flutter 3.47 đánh dấu nó stable, kèm cache build cục bộ và một API theme trừu tượng khiến tính năng này đáng nhìn lại. Nếu bạn làm design system, thư viện component, hay bất cứ thứ gì có hơn ba trạng thái hiển thị, thứ này thay đổi vòng lặp làm việc của bạn.

Lời hứa của nó hẹp và trung thực: previewer render từng widget riêng lẻ, không cần khởi động app, không cần điều hướng tới đúng màn hình, không cần giả lập đúng state. Đó là việc khác với hot reload, và là việc hot reload xưa nay vẫn làm dở.

Khởi động previewer

Từ IDE — Android Studio, IntelliJ, hay VS Code — previewer tự chạy; mở tab Flutter Widget Preview ở sidebar. Từ terminal:

flutter widget-preview start

Lệnh này dựng một server cục bộ và mở môi trường xem trước trực tiếp trong trình duyệt. Build được cache trong thư mục .widget_preview/ của project — cache này chính là lý do khởi động nhanh lên rõ rệt ở 3.47, và là thứ bạn nên thêm vào .gitignore nếu tooling chưa tự làm.

Bạn có thể annotate cái gì

@Preview đến từ package:flutter/widget_previews.dart và áp dụng được cho:

  • hàm top-level trả về Widget hoặc WidgetBuilder
  • static method trong class trả về Widget hoặc WidgetBuilder
  • constructor và factory public của widget không có tham số bắt buộc

Trường hợp đơn giản nhất:

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

@Preview(name: 'My Sample Text')
Widget mySampleText() {
  return const Text('Hello, World!');
}

Không app, không MaterialApp, không route. Lưu file là preview cập nhật.

Đầy đủ tham số, và chúng thực sự dùng để làm gì

Class Preview nhỏ gọn, đó là dấu hiệu tốt. Đây là toàn bộ bề mặt API:

Tham sốKiểuTác dụng
groupStringGom nhóm các preview liên quan. Mặc định 'Default'.
nameString?Nhãn hiển thị cạnh preview.
sizeSize?Ràng buộc kích thước nhân tạo áp lên widget.
textScaleFactordouble?Tỉ lệ phóng chữ, để kiểm tra accessibility.
wrapperWidgetWrapper?Bọc widget trong một cây widget — scaffold, provider, InheritedWidget.
themePreviewTheme?Trả về dữ liệu theme Material và Cupertino để áp dụng.
brightnessBrightness?Độ sáng sáng/tối ban đầu.
localizationsPreviewLocalizations?Cấu hình localization cho preview.

wrapper là thứ khiến preview dùng được trong codebase thật. Phần lớn widget không đứng một mình — chúng cần Scaffold, cần theme, hoặc cần một repository được inject. Bạn cung cấp một lần:

@Preview(
  name: 'Submit button — trạng thái đang bấm',
  group: 'Form Controls',
  size: Size(240, 56),
  textScaleFactor: 1.5,
  wrapper: _inScaffold,
)
Widget submitButtonPreview() => const SubmitButton(isBusy: true);

Widget _inScaffold(Widget child) => MaterialApp(
      home: Scaffold(body: Center(child: child)),
    );

Render cùng một widget theo nhiều cách

Đây là chỗ preview thắng hot reload thẳng thừng. Xếp chồng annotation để có một widget được render dưới nhiều cấu hình cùng lúc:

@Preview(group: 'Brightness', name: 'Light', brightness: Brightness.light)
@Preview(group: 'Brightness', name: 'Dark', brightness: Brightness.dark)
Widget buttonPreview() => const ButtonShowcase();

Khi cùng ba bốn biến thể đó lặp lại qua hàng chục component, hãy nâng chúng thành một MultiPreview:

final class MultiBrightnessPreview extends MultiPreview {
  const MultiBrightnessPreview();

  @override
  List<Preview> get previews => const [
        Preview(group: 'Brightness', name: 'Light', brightness: Brightness.light),
        Preview(group: 'Brightness', name: 'Dark', brightness: Brightness.dark),
      ];
}

@MultiBrightnessPreview()
Widget buttonPreview() => const ButtonShowcase();

Nhúng design system vào một annotation riêng

API theme trừu tượng thêm ở 3.47 là mảnh ghép giúp thứ này mở rộng được. Thay vì lặp theme: trên mọi preview, hãy kế thừa Preview và cấp builder một lần:

final class MyCustomPreview extends Preview {
  const MyCustomPreview({
    super.name,
    super.group,
    super.size,
    super.textScaleFactor,
    super.wrapper,
    super.brightness,
    super.localizations,
  }) : super(theme: MyCustomPreview.themeBuilder);

  static PreviewThemeData themeBuilder() {
    return PreviewThemeData(
      materialLight: ThemeData.light(),
      materialDark: ThemeData.dark(),
    );
  }
}

Giờ mọi @MyCustomPreview(...) trong codebase đều render theo token thật của bạn. PreviewThemeData mang cả dữ liệu Material lẫn Cupertino, điều này ở 3.47 quan trọng hơn trước — khi design system chuyển sang package độc lập material_uicupertino_ui, preview là cách rẻ để xem một component có thực sự sống sót ở cả hai hay không.

Còn có transform(), cho phép annotation tuỳ biến viết lại preview lúc chạy — hữu ích khi muốn thêm tiền tố vào tên hoặc đổi theme cho cả một lớp preview mà không đụng vào chỗ gọi.

Preview không phải golden test

Cần nói thẳng, vì nhiều nhóm nhầm hai thứ này. Preview là công cụ soạn thảo: nhanh, trực quan, có con người trong vòng lặp, không có assertion. Golden test là công cụ chống hồi quy: chậm, headless, và làm đỏ CI khi một pixel xê dịch. Preview không thay thế golden, và một preview “chạy được” chẳng chứng minh gì ngoài việc nó render ra.

Cặp đôi hữu ích là: preview trong lúc xây component, golden khi các trạng thái đã ổn định.

Những giới hạn bạn sẽ gặp

Previewer chạy trong môi trường nền web, nên có ranh giới cứng:

  1. Không dùng được native plugin, và không có dart:io hay dart:ffi. Mọi thứ chạm tới filesystem, platform channel, hay FFI đều không render — hãy inject một bản giả qua wrapper.
  2. Tham số callback phải public và constant. Closure private sẽ không được nhận diện.
  3. Đường dẫn asset phải theo package: dùng 'packages/my_package_name/assets/my_image.png', không phải 'assets/my_image.png'.
  4. Widget không có ràng buộc kích thước sẽ tự bị giới hạn ở khoảng 50% chiều cao và chiều rộng previewer. Truyền size khi điều đó bóp méo layout.
  5. Chỉ hỗ trợ một project hoặc một Pub workspace. Hỗ trợ đa project trong IDE vẫn đang được nghiên cứu, nên một monorepo lớn có thể không sáng đèn hết.

Áp dụng mà không phải viết lại

  1. Nâng lên 3.47 và chạy flutter widget-preview start một lần để chắc previewer build được project.
  2. Thêm .widget_preview/ vào .gitignore.
  3. Chọn một component lá — nút bấm, badge, list tile — và thêm đúng một @Preview. Đừng bắt đầu từ cả màn hình.
  4. Viết một wrapper cài theme và provider của app, rồi tái sử dụng.
  5. Nâng các biến thể lặp lại (sáng/tối, text scale 1.0/2.0, LTR/RTL) thành MultiPreview.
  6. Kế thừa Preview với PreviewThemeData của design system và chuẩn hoá theo annotation đó.
  7. Thêm golden test cho những trạng thái bạn đã biết là đúng.

Kết luận

Widget Previews thôi là bản demo ở 3.47. Cache khiến thời gian khởi động chấp nhận được, và các API theme là ranh giới giữa một món đồ chơi với thứ mà một nhóm design system có thể chuẩn hoá. Các giới hạn là thật — không native code, asset phải theo package, chỉ một workspace — nhưng đó là giới hạn về phạm vi, không phải về độ chín. Nếu bạn từng thử preview ở 3.35 rồi nhún vai, lời khuyên thành thật là thử lại: đây là bản phát hành mà tính năng này xứng đáng có chỗ trong vòng lặp làm việc.


Bài viết gốc đăng tại FlutterCook. Bản trên đó là bản được cập nhật mới nhất.

Nhận xét

Bài đăng phổ biến từ blog này

Thiết kế giao diện với DotNetBar (Phần 1)

Đây là phiên bản DotNetBar hỗ trợ C# và Visual Basic https://www.dropbox.com/s/wx80jpvgnlrmtux/DotNetBar.rar  , phiên bản này hỗ trợ giao diện Metro cực kỳ “dễ thương” Các bạn load về và cài đặt, khi cài đặt xong sẽ có source code mẫu của tất cả các control. Để sử dụng được các control của DotNetBar các bạn nhớ add item vào controls box. Thiết kế giao diện với DotNetBar, giao diện sẽ rất đẹp. Link các video hướng dẫn chi tiết cách sử dụng và coding: http://www.devcomponents.com/dotnetbar/movies.aspx Hiện tại DotNetBar có rất nhiều công cụ cực mạnh, trong đó có 3 công cụ dưới đây: DotNetBar for Windows Forms Requires with Visual Studio 2003, 2005, 2008, 2010 or 2012.   DotNetBar for WPF Requires with Visual Studio 2010 or 2012 and Windows Presentation Foundation.   DotNetBar for Silverlight Requires with Visual Studio 2010 or 2012 and Silverlight. Dưới đây là một số hình ảnh về các control trong DotnetBar.   Metro User Interface  controls with Metro Tiles, toolba...

5 concepts every Flutter dev should know

  Phụ lục: State management architecture Testing IDE Shortcuts Platform channel Maintaining a project Tôi đã làm việc với Flagship trong một thời gian dài, và đây là những điều mà tôi phát hiện ra là điều cần phải có đối với bất kỳ nhà phát triển Flagship nào, về tổng thể nó sẽ khiến bạn trở thành một nhà phát triển Flagship giỏi trong thời gian dài. 1. State management architecture Đây là một trong những chủ đề quan trọng nhất trong cộng đồng thiết bị rung, nó khá quan trọng nếu bạn muốn duy trì một dự án rung kích thước trung bình hoặc lớn. Nó sẽ giúp tạo một dự án suôn sẻ và thêm các tính năng mới một cách hoàn hảo.  2. Testing Đây là một chủ đề duy nhất mà tôi không hiểu tại sao nó lại quan trọng trước đó trong sự nghiệp của tôi, nhưng khi tôi tiến lên trong sự nghiệp của mình và có kinh nghiệm với nhiều dự án và vấn đề xảy ra trong môi trường sản xuất. Tôi đã nhận ra một cách khó khăn, tại sao điều này lại quan trọng như vậy. Nếu bạn vẫn muốn có thêm lý do để cân nhắc thử...

Announcing Flutter 2

  Phụ lục: Flutter on the web Flutter 2 on desktops, foldables, and embedded devices The growing Flutter ecosystem Dart: The secret sauce behind Flutter Flutter 2: Available now Hôm nay, chúng tôi sẽ công bố Flutter 2: một bản nâng cấp lớn cho Flutter cho phép các nhà phát triển tạo các ứng dụng đẹp, nhanh chóng và di động cho bất kỳ nền tảng nào. Với Flutter 2, bạn có thể sử dụng cùng một cơ sở mã để gửi các ứng dụng gốc cho năm hệ điều hành: IOS, Android, Windows, macOS và Linux; cũng như trải nghiệm web nhắm mục tiêu các trình duyệt như Chrome, Firefox, Safari hoặc Edge. Flutter thậm chí có thể được nhúng vào ô tô, TV và thiết bị gia dụng thông minh, mang đến trải nghiệm di động và lan tỏa nhất cho thế giới điện toán xung quanh. Mục tiêu của chúng tôi là thay đổi cơ bản cách các nhà phát triển nghĩ về việc xây dựng ứng dụng, bắt đầu không phải với nền tảng bạn đang nhắm mục tiêu mà là với trải nghiệm bạn muốn tạo. Flutter cho phép bạn tạo ra những trải nghiệm tuyệt đẹp trong đó ...