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

Bài đăng

Đang hiển thị bài đăng từ Tháng 9, 2026

Secrets in a Flutter app: what you can store, and what you cannot

Start with the uncomfortable part, because everything else follows from it. Any string compiled into your app is public. Not “hard to find” — public. An APK is a zip file; strings on the extracted binary takes seconds. --dart-define values, constants, obfuscated names, base64-encoded blobs: all of it is recoverable by anyone motivated enough to download your app once. This is not a Flutter weakness. It is true of every client application on every platform. What it changes is where you draw the line between “the app knows this” and “the app can ask for this”. The line Kind of secret Where it belongs Third-party API key with billing attached Server only. The app calls your backend, your backend calls them Public/publishable keys (Stripe publishable, Firebase config, Maps key) In the app — they are designed for it, and restricted server-side User session token Device secure storage, short-lived, refreshable Refresh token Device secure storage, revoc...

Push notifications in Flutter: the four states your app can be in

The notification arrives. Sometimes the app shows it, sometimes the system does, sometimes tapping it opens the right screen and sometimes it dumps the user on the home page. The behaviour feels random until you see the structure underneath: the same message takes four different paths depending on what the app was doing. App state Who displays it Which handler runs Foreground Nobody, by default onMessage Background The OS onBackgroundMessage (separate isolate) Terminated The OS onBackgroundMessage (separate isolate) Opened by tapping — onMessageOpenedApp , or getInitialMessage if it was terminated Every notification bug I have debugged was one of those rows being unhandled. Wire all four and most of the mystery disappears. Notification messages versus data messages Before the code, the distinction that determines everything: an FCM payload can contain a notification block, a data block, or both. notification present — the OS displays it automa...

go_router and deep links: the parts the quickstart leaves out

Every routing tutorial ends at the same place: a GoRouter with three routes, a context.go('/details/42') , and a screenshot of it working. That part takes ten minutes. The parts that take the rest of the week are the ones nobody demos — an auth redirect that does not fight the login screen, a bottom navigation bar where each tab keeps its own history, and the platform configuration that decides whether https://yourapp.com/order/7 opens your app or Safari. The routing table, and where state actually lives Start with the shape that scales, which is a top-level router object that is not rebuilt: final _rootKey = GlobalKey < NavigatorState >(); final _shellKey = GlobalKey < NavigatorState >(); final router = GoRouter ( navigatorKey : _rootKey, initialLocation : '/feed' , debugLogDiagnostics : true , routes : [ /* ... */ ], errorBuilder : (context, state) => NotFoundScreen (uri : state.uri), ); class App extends Stat...

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

Flutter localization with ARB files: plurals, genders, and the parts that bite

The first localization commit in most Flutter apps is a Map<String, String> keyed by language code, looked up through a global. It works for two languages and a hundred strings. It breaks the first time somebody needs “1 item” versus “2 items”, and it breaks badly the first time a translator asks what home_screen_label_2 is for. Flutter’s official answer is ARB files compiled by gen_l10n into a generated Dart class. The setup is short. What is worth understanding is the parts the quickstart does not cover: ICU plurals, placeholder types, locale resolution, and what happens on the day someone adds Arabic. Setup, once # pubspec.yaml dependencies : flutter_localizations : sdk : flutter intl : any flutter : generate : true # l10n.yaml at the project root arb-dir : lib/l10n template-arb-file : app_en.arb output-localization-file : app_localizations.dart nullable-getter : false nullable-getter: false is worth setting deliberately. With it, AppLocalizations...