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

Bài đăng

Key trong Flutter: một quy tắc duy nhất giải thích mọi trường hợp

Có một con bug rất cụ thể dạy cho tất cả mọi người về key. Bạn có một danh sách các dòng có trạng thái — mỗi dòng một checkbox, hoặc một expansion tile, hoặc một ô nhập liệu. Bạn xóa dòng thứ hai. Dòng đó biến mất đúng như mong đợi, nhưng dấu tích vốn thuộc về nó giờ lại nằm trên một dòng khác. Dữ liệu chẳng có gì sai. Tải lại trang thì mọi thứ bình thường. Đó không phải lỗi của Flutter. Đó là framework làm đúng những gì được bảo, và cách sửa chỉ dài một từ. Nhưng cách sửa chỉ thật sự dính lại nếu bạn hiểu quy tắc bên dưới, vì cùng quy tắc đó giải thích vì sao đôi khi key chẳng làm gì cả, vì sao GlobalKey đắt, và vì sao PageStorageKey không thật sự là key theo cùng nghĩa. Ba cây, và cái cây giữ state của bạn Flutter duy trì ba cấu trúc song song. Cây widget là kết quả build của bạn: các đối tượng cấu hình bất biến, bị vứt đi và tạo lại liên tục. Cây render lo layout và vẽ. Nằm giữa hai cái đó là cây element , và đây mới là cái quan trọng ở bài này, vì một Element sống lâu và ...
Các bài đăng gần đây

Mua hàng trong ứng dụng Flutter: những phần không nằm ở cái nút

Phiên bản trong bài hướng dẫn của việc mua hàng trong ứng dụng là một cái nút, một lời gọi buyNonConsumable , và một callback thành công đặt isPremium = true . Phát hành thứ đó rồi bạn sẽ lần lượt phát hiện: người dùng cài lại thì mất sạch, một giao dịch bị cuộc gọi cắt ngang thì không bao giờ hoàn tất, người đã hoàn tiền vẫn giữ quyền truy cập mãi mãi, và bất kỳ ai có bản build đã sửa đều mở khoá ứng dụng miễn phí. Không cái nào trong số đó là trường hợp biên. Chúng là điều kiện vận hành bình thường của một hệ thống thanh toán. Stream mới là API in_app_purchase không hoạt động theo kiểu hỏi-đáp. Giao dịch đến trên một stream, bao gồm cả giao dịch bạn không khởi tạo trong phiên này — các lần khôi phục, giao dịch hoàn tất khi ứng dụng đã đóng, và giao dịch từ thiết bị khác cùng tài khoản. final class PurchaseService { PurchaseService ( this ._iap, this ._backend); final InAppPurchase _iap; final BackendApi _backend; StreamSubscription < List < Purchase...

Impeller thành renderer desktop mặc định — thực sự đổi gì

Impeller mặc định trên mobile đã có từ trước. Flutter 3.47 hoàn tất trên macOS, Windows và Linux . App desktop giờ nhắm Metal (macOS) và Vulkan (Windows/Linux) với tập shader cố định, compile lúc build engine. Bạn được gì Hết jank compile shader khi animation chạy lần đầu. Chữ SDF — glyph sắc hơn trên màn desktop mật độ pixel thấp hơn. Wide Gamut Color mặc định trên macOS khi phần cứng hỗ trợ. Opt-out Skia tạm thời Nền tảng Cách tắt macOS FLTEnableImpeller=false trong Info.plist Windows project.set_impeller_switch(flutter::ImpellerSwitch::Disabled) trong main.cpp Linux fl_dart_project_set_enable_impeller(project, FALSE) trong my_application.cc Hãy file bug. Fallback sẽ bị gỡ. Nên test lại FragmentShader tùy chỉnh và effect nặng saveLayer . Platform view / external texture. Luồng print/export từng giả định color management của Skia. Chạy profile mode trước. Frame vẫn dài thì vẫn là UI vs raster — Impeller chỉ lấy việc ở nhánh compile shader ,...

Keys in Flutter: the one rule that explains every case

There is a specific bug that teaches everyone about keys. You have a list of stateful rows — each with a checkbox, or an expansion tile, or a text field. You delete the second row. The row disappears correctly, but the checkmark that belonged to it is now sitting on a different row. Nothing about your data is wrong. Reload the page and everything is fine. That bug is not a Flutter defect. It is the framework doing exactly what it was told, and the fix is one word long. But the fix only sticks if you understand the rule underneath it, because the same rule explains why keys sometimes do nothing at all, why GlobalKey is expensive, and why a PageStorageKey is not really a key in the same sense. Three trees, and the one that holds your state Flutter maintains three parallel structures. The widget tree is your build output: immutable configuration objects, thrown away and recreated constantly. The render tree does layout and painting. Between them sits the element tree , and that i...

In-app purchases in Flutter: the parts that are not about the button

The tutorial version of in-app purchases is a button, a call to buyNonConsumable , and a success callback that sets isPremium = true . Ship that and you will find out, in order: that users who reinstall lose everything, that a purchase interrupted by a phone call never completes, that a refunded user keeps their access forever, and that anyone with a modified build unlocks the app for free. None of those are edge cases. They are the normal operating conditions of a payments system. The stream is the API in_app_purchase does not work as request-response. Purchases arrive on a stream, including purchases you did not initiate in this session — restores, purchases completed while the app was closed, and purchases from a different device on the same account. final class PurchaseService { PurchaseService ( this ._iap, this ._backend); final InAppPurchase _iap; final BackendApi _backend; StreamSubscription < List < PurchaseDetails >> ? _sub; void st...

Impeller is now the default desktop renderer — what actually changes

Mobile Impeller defaults landed earlier. Flutter 3.47 finishes the job on macOS, Windows, and Linux . Desktop apps now target Metal (macOS) and Vulkan (Windows/Linux) with a fixed shader set compiled at engine-build time. What you gain No shader-compilation jank on first run of an animation. Signed Distance Function text — sharper glyphs on lower-density desktop displays. Wide Gamut Color on by default on macOS where hardware supports it. Temporary Skia opt-outs If a regression blocks you: Platform Opt-out macOS FLTEnableImpeller=false in Info.plist Windows project.set_impeller_switch(flutter::ImpellerSwitch::Disabled) in main.cpp Linux fl_dart_project_set_enable_impeller(project, FALSE) in my_application.cc File a bug. Fallbacks are scheduled for removal. What to retest Custom FragmentShader s and saveLayer -heavy effects. Platform views and external textures. Print/export paths that assumed Skia color management. Profile mode first. If a f...

Đường ống ảnh trong Flutter: từ một URL tới pixel trên màn hình

Image.network(url) là một trong những API thân thiện nhất của Flutter và cũng là một trong những cái dễ đánh bại bạn nhất. Nó chạy được ngay, rồi một tháng sau bạn có những tấm ảnh loé trắng mỗi lần rebuild, một thư viện ảnh tải lại khi cuộn ngược, và 400 MB bộ nhớ trên một máy 3 GB. Cả ba đều là cùng một hiểu nhầm: Image là widget, nhưng việc cache và giải mã diễn ra trong một đối tượng mà nó uỷ thác — ImageProvider . Biết đối tượng đó đánh khoá theo cái gì, và cache cái gì, sẽ giải thích được tất cả. Năm giai đoạn Khoá. ImageProvider sinh ra một khoá — với NetworkImage là URL cộng với scale. Hai provider có khoá bằng nhau là cùng một ảnh dưới góc nhìn của cache. Tải. Byte đến từ mạng, từ asset bundle, từ file, hoặc từ bộ nhớ. Giải mã. Byte đã nén trở thành một dart:ui.Image : pixel thô, rộng × cao × 4 byte. Cache. Ảnh đã giải mã đi vào PaintingBinding.instance.imageCache , đánh khoá theo bước 1. Vẽ. Widget vẽ nó ra, áp dụng fit , alignment , và mọi bộ lọc màu. Hệ ...

Hybrid Composition++: sửa platform view Android hết jank

Nhúng WebView hoặc Google Map từng là đánh đổi: Virtual Display (rét hình, input lạ) hoặc Hybrid Composition (CPU cao hơn). Hybrid Composition++ (HCPP) để Android tự compositing layer qua Vulkan hardware-buffer swapchain và SurfaceControl transaction, đồng bộ với frame Flutter. Bật HCPP < meta-data android:name = "io.flutter.embedding.android.EnableHcpp" android:value = "true" /> Hoặc chạy --enable-hcpp . Không có API Dart mới — platform view hiện có được nâng cấp. Yêu cầu và giới hạn Bị chặn bởi API level và phần cứng; không phải máy nào cũng dùng được dù đã opt-in. Nội dung SurfaceView trở nên khả thi — mode cũ rất khó. Sẽ thành mặc định sau này — hãy validate luồng map/WebView sản phẩm ngay. Đo gì FPS scroll bên trong WebView /map, không chỉ list Flutter quanh nó. Độ chính xác chạm gần mép view nhúng. Bàn phím show/hide đè lên platform view. Nếu vẫn rét hình, kiểm tra cờ có trong manifest đã build (flavor có thể ghi đè) và m...