Flutter Tips and Tricks

repository·main·Indexed 25 days ago

https://github.com/bizz84/flutter-tips-and-tricks

A curated collection of Flutter tips and tricks covering topics such as managing multiple Flutter versions with Puro, implementing release toggles with dart-define, using ValueNotifier for side effects, and configuring CI/CD on Codemagic. Includes guidance on Flutter 3.27 and 3.29 updates, including the new spacing argument in Row and Column and Kotlin DSL support for Android projects.

Tokens
94.2K
Snippets
217
Records
560
Agent score
80%

What's inside flutter-tips-and-tricks

  1. Evaluate FlutterFlow for your project

    main

    FlutterFlow is a low-code platform for building Flutter apps. While it excels at rapid prototyping and has strong Firebase integration, it has significant limitations for production-grade software development.

    Pros

    • Rapid Prototyping: Quickly build UI prototypes.
    • Documentation: High-quality documentation and tutorials.
    • Firebase Integration: Strong support for Firebase services.
    • UI Tools: Drag & Drop UI and decent theming support.

    Cons

    • Unmaintainable Code: Generated code often contains spaghetti code, massive widgets, linter rule violations, and 'god-like' state classes (e.g., FFAppState).
    • Lack of Control: The platform makes decisions for you regarding dependencies, state management, routing, and data persistence.
    • Limited Version Control: The project history feature is a basic version control system not suitable for serious development.
    • Poor Debugging: Debugging can be difficult, often requiring debugging main.dart.js via Chrome DevTools.
    • One-Way Sync: Once you download the code and edit it in an IDE, you cannot sync those changes back to the FlutterFlow editor.
    • Production Readiness: It is difficult to build truly production-ready apps that require complex logic or long-term maintenance.

    Summary Recommendation

    • If you know how to code: Write code directly in Flutter instead of using FlutterFlow.
    • If you don't know how to code: Learn to code instead of relying on FlutterFlow.
  2. What is WidgetsBindingObserver and what can it track?

    main

    The WidgetsBindingObserver mixin allows you to listen to various system-level changes in a Flutter application. It is useful for responding to:

    • Routing events: Changes in the navigation stack.
    • Device orientation: When the user rotates the device.
    • Platform settings: Changes to text scale factor, brightness, locale, etc.
    • App lifecycle events: When the app moves between foreground, background, or paused states.
  3. Layout with Stack and FractionallySizedBox

    main
    To create layouts where children occupy a specific percentage of the available space within a Stack, use the FractionallySizedBox widget. This is useful for creating responsive UI elements that scale relative to their parent container.
  4. Optimize rebuilds using MediaQuery.maybeOf() or specific property accessors

    main

    By default, calling MediaQuery.of(context) inside a build method causes the widget to rebuild whenever any property of MediaQueryData changes (such as screen size, orientation, or device pixel ratio). This can lead to unnecessary rebuilds.

    To minimize rebuilds, depend only on the specific properties you need. In newer Flutter versions (3.10+), you can use more granular accessors to ensure the widget only rebuilds when the specific data it consumes actually changes.

  5. How TextEditingController works with AnimatedBuilder

    main

    The AnimatedBuilder widget requires a Listenable object passed to its animation argument to know when to trigger rebuilds.

    Because TextEditingController extends ValueNotifier, which in turn extends ChangeNotifier, it implements the Listenable interface. This makes it a perfect candidate for AnimatedBuilder. When the user types, the TextEditingController notifies its listeners, triggering the AnimatedBuilder to execute its builder function and refresh the UI with the latest text state.

  6. When to use global variables in Flutter

    main

    Global variables are acceptable in Flutter projects when they are used for immutable data or dependency injection containers.

    Safe use cases:

    • const widgets: Defining reusable UI components globally.
    • Riverpod providers: Using global providers for state management.

    Dangerous use cases:

    • Global mutable state: Avoid global variables that can be changed from anywhere. This makes it difficult to track which part of the code modified the state and which widgets need to rebuild, leading to unpredictable side effects and debugging difficulties.
  7. Use wildcard variables in Dart 3.7

    main

    In Dart 3.7 and later, the _ character acts as a wildcard variable (a true placeholder). This allows you to use _ multiple times in the same scope (such as in parameter lists) without causing name collisions. However, because it is strictly a placeholder, you cannot use _ as an actual variable name to access its value.

    // Before Dart 3.7, you had to use unique names like __ or ___
    // Since Dart 3.7, you can use multiple underscores for unused parameters:
    SliverAnimatedGrid(
      itemBuilder: (_, _, _) {
        return Placeholder();
      }
    )
  8. When to use Provider Overrides vs FutureProvider

    main

    Choosing between a provider override and a FutureProvider depends on what you are initializing:

    • Use Provider Overrides for dependencies (e.g., Repositories, Data Sources, Databases). These are typically initialized once at app startup. Overriding allows you to access them synchronously via ref.watch or ref.read without handling AsyncValue states.
    • Use FutureProvider for data fetching (e.g., fetching a list of users from an API). Data fetching often requires handling loading states, error states, and the ability to reload or refresh the data.
  9. Understand the difference between kIsWeb and defaultTargetPlatform

    main

    To correctly handle platform-specific logic in Flutter, you must distinguish between the runtime environment and the underlying operating system:

    • kIsWeb: A boolean constant that is true if the application is running in a web browser.
    • defaultTargetPlatform: Returns a TargetPlatform enum indicating which platform the browser (or device) is running on.

    Using kIsWeb first ensures you don't trigger dart:io errors on the web, while defaultTargetPlatform allows you to detect the OS (like iOS or Android) even when running via a web browser.

  10. Navigate with GoRouter: go vs push

    main

    When using the go_router package for navigation:

    • go: Uses imperative navigation to change the underlying route. It replaces the current navigation stack with the new route, which is useful for deep linking or jumping to a specific location in the app hierarchy.
    • push: Pushes a new route onto the existing navigation stack. This allows the user to go 'back' to the previous screen using the standard back button behavior.
  11. Understand how `kIsWeb` is implemented in Flutter

    main

    The kIsWeb constant is a top-level boolean used to detect if the Flutter application is running on a web platform. It is implemented using a constant comparison between an integer and a double:

    const bool kIsWeb = identical(0, 0.0);

    This works because of how types are handled across different runtimes:

    • On Web: JavaScript does not have a distinct integer type; both integers and doubles are backed by the same numeric object type. Therefore, identical(0, 0.0) returns true.
    • On Dart VM (Mobile/Desktop): Integers (int) and doubles (double) are distinct types. Therefore, identical(0, 0.0) returns false.

    Use this constant to conditionally execute code that is only compatible with web browsers or mobile/desktop environments.