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

Bài đăng

Hiển thị các bài đăng có nhãn Debugging

Finding a Flutter memory leak: the five objects that never get disposed

A memory leak in Flutter rarely announces itself. The app works, tests pass, and then somebody navigates between two screens forty times and the process is at 900 MB. On a mid-range Android device that ends as an out-of-memory kill, reported to you as “the app closed by itself” with no stack trace. Dart is garbage collected, so a leak here means exactly one thing: something still holds a reference to an object you are done with . The whole investigation is finding what that something is. The five patterns In practice, almost every leak I have chased in a Flutter app was one of these. 1. A controller that is never disposed. AnimationController , TextEditingController , ScrollController , TabController , PageController — every one of them holds listeners and, in the animation case, a ticker registered with the scheduler. class _EditorState extends State < Editor > with SingleTickerProviderStateMixin { late final _text = TextEditingController (); late final _a...

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...

Why your tap doesn't register: hit testing and the gesture arena

There is a specific kind of Flutter bug that eats an afternoon. The widget is on screen. The onTap is wired. You add a print statement and it never fires. Nothing in the console, no error, no warning — the tap simply does not exist. Every instance of this is one of two causes. Either the pointer never reached your detector during hit testing, or it reached it and the detector lost the gesture arena to something else. They are different problems with different fixes, and they are distinguishable. How a pointer finds a widget When a finger goes down, the framework walks the render tree from the root, asking each render object whether the point is inside it. The result is a hit test path : an ordered list from the deepest hit object up to the root. Pointer events are then dispatched along that path. Three rules explain most surprises: Hit testing is geometric, and it uses the render object’s box. Not its visual appearance. A Container with no colour and no child has zero size, s...

Flutter error handling: catching what actually reaches users

A crash-free rate of 99.8% sounds excellent until you realise it only counts crashes your reporting tool was wired to see. In Flutter, an error can escape through four different doors, and most apps only guard one or two of them. The four doors Door Catches Missed if unwired FlutterError.onError Errors inside the framework: build, layout, paint, gesture callbacks Red screens, silent layout failures PlatformDispatcher.instance.onError Uncaught async errors in the root zone Most Future failures Isolate.current.addErrorListener Errors in isolates you spawned Every background-compute failure Native crash handler Platform-level crashes, plugin native code Anything that kills the process Here is all four, wired once at startup: Future < void > main () async { WidgetsFlutterBinding . ensureInitialized (); await Firebase . initializeApp (); final crashlytics = FirebaseCrashlytics .instance; // 1. Framework errors. FlutterError .onEr...

BuildContext is an element: reading the error messages that mention it

BuildContext is the parameter everyone types a thousand times without ever asking what it is. It shows up in every build method, it is required by Theme.of , Navigator.of , showDialog , MediaQuery.sizeOf — and then one day it produces an error that makes no sense, like Scaffold.of() failing inside a widget that is very obviously wrapped in a Scaffold . The declaration answers the whole thing. In the framework source, abstract class Element extends DiagnosticableTree implements BuildContext . A BuildContext is an Element , exposed through a narrow interface so you cannot mutate the tree with it. When a build method receives a context, it is being handed its own element — its exact position in the live tree. Everything confusing about BuildContext becomes obvious once you read it as “my node in the tree” rather than “the app.” Lookups walk upward from your node Theme.of(context) , MediaQuery.of(context) , Navigator.of(context) and friends all do the same thing: start at tha...