Skip to content

Commit 6f4b909

Browse files
committed
Add method channels info
1 parent 6f457ec commit 6f4b909

File tree

1 file changed

+60
-64
lines changed

1 file changed

+60
-64
lines changed

docs/ff-concepts/existing-flutter/method-channels.md

Lines changed: 60 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ With MethodChannels, you can:
2121
* Perform operations that need native performance or device-specific access.
2222
* Return data from the native side into Flutter with low latency.
2323

24-
### Why this matters for native engineers {#why-this-matters-for-native-engineers}
24+
**Why this matters for native engineers**
2525

2626
| What you need | How MethodChannel helps |
2727
| :---- | :---- |
@@ -33,7 +33,7 @@ With MethodChannels, you can:
3333
You’re not limited by what Flutter provides out of the box. MethodChannels lets you plug in your native knowledge exactly where needed, so you don’t lose years of platform experience when moving to Flutter.
3434

3535

36-
## **What is a MethodChannel?** {#what-is-a-methodchannel?}
36+
## What is a MethodChannel?
3737

3838
A **MethodChannel** is Flutter’s core mechanism for integrating platform-specific functionality. It allows Dart code to send messages to, and receive responses from, the host platform’s native code \- Android (written in Kotlin or Java) or iOS (written in Swift or Objective-C). This enables your Flutter app to access device features and third-party native libraries that are outside the scope of the Flutter framework or its plugin ecosystem. Here is an example of MethodChannel.
3939

@@ -378,7 +378,6 @@ After implementing and testing your plugin:
378378
379379
Ensure your `pubspec.yaml` is correctly configured, and consider tagging releases for versioning.
380380
381-
---
382381
383382
**Step 2: Add the Plugin as a Dependency in FlutterFlow**
384383
@@ -389,19 +388,22 @@ To integrate your custom plugin into a FlutterFlow project:
389388
3. In the **Settings** panel on the right, scroll to **Dependencies**.
390389
4. Add your plugin using the Git URL:
391390
392-
`My_custom_plugin:`
393-
`git:`
394-
`url: https://github.com/yourusername/my_custom_plugin.git`
391+
```yaml
392+
my_custom_plugin:
393+
git:
394+
url: https://github.com/yourusername/my_custom_plugin.git
395+
```
395396
396397
5. In the code editor, import your plugin:
397398
398-
`import 'package:my_custom_plugin/my_custom_plugin.dart';`
399+
`import 'package:my_custom_plugin/my_custom_plugin.dart';`
400+
401+
399402
400403
6. Implement the desired functionality using the plugin's API.
401404
402405
For detailed guidance, refer to FlutterFlow's documentation on [using unpublished or private packages](https://docs.flutterflow.io/concepts/custom-code/#using-unpublished-or-private-packages).
403406
404-
---
405407
406408
**Step 3: Utilize the Plugin via Custom Actions in FlutterFlow**
407409
@@ -410,17 +412,20 @@ With the plugin integrated, you can now create Custom Actions to leverage its fu
410412
1. Define a new Custom Action in FlutterFlow.
411413
2. In the code editor, implement the action using your plugin. For example:
412414
413-
`Future<int> getBatteryLevel() async {`
414-
`final batteryLevel = await MyCustomPlugin.getBatteryLevel();`
415-
`return batteryLevel;`
416-
`}`
415+
```js
416+
Future<int> getBatteryLevel() async {
417+
final batteryLevel = await MyCustomPlugin.getBatteryLevel();
418+
return batteryLevel;
419+
}
420+
421+
```
422+
417423
418424
3. Compile the custom code to ensure there are no errors.
419425
4. Use this Custom Action within your FlutterFlow project's action flows, just like any built-in action.
420426
421427
This approach allows you to encapsulate complex logic within reusable actions, enhancing modularity and maintainability.
422428
423-
---
424429
425430
### Managing Private Repositories
426431
@@ -432,56 +437,52 @@ If your plugin repository is private, FlutterFlow needs access to it. As per Flu
432437
433438
MethodChannels are powerful but require careful implementation. When the Dart and native sides are not aligned, or error handling is overlooked, it often leads to runtime issues or silent failures. This section outlines the most common problems developers face with MethodChannels—especially in projects generated by tools like FlutterFlow—and provides actionable solutions to help you debug effectively and write resilient platform-channel integrations.
434439
435-
---
436440
437-
- **MissingPluginException**
441+
### MissingPluginException
438442
439-
**Symptom:** Flutter throws a `MissingPluginException`, typically saying the plugin or method is not implemented.
443+
**Symptom:** Flutter throws a `MissingPluginException`, typically saying the plugin or method is not implemented.
440444
441-
**What it means:** Flutter tried to invoke a method on the MethodChannel, but the native side did not recognize the channel or method name.
445+
**What it means:** Flutter tried to invoke a method on the MethodChannel, but the native side did not recognize the channel or method name.
442446
443-
**Common causes:**
447+
**Common causes:**
444448
445-
* Dart `MethodChannel` name does not match the native channel name.
446-
* Native code handler (`setMethodCallHandler`) was never set up or was incorrectly placed.
447-
* Custom native code was overwritten when re-downloading a FlutterFlow project without preserving changes.
448-
* The method was invoked before the Flutter engine or the channel was fully initialized.
449-
* Hot reload only updates Dart code, but not with native channel implementations.
449+
* Dart `MethodChannel` name does not match the native channel name.
450+
* Native code handler (`setMethodCallHandler`) was never set up or was incorrectly placed.
451+
* Custom native code was overwritten when re-downloading a FlutterFlow project without preserving changes.
452+
* The method was invoked before the Flutter engine or the channel was fully initialized.
453+
* Hot reload only updates Dart code, but not with native channel implementations.
450454
451-
**How to fix:**
455+
**How to fix:**
452456
453-
* Confirm that the channel name is **identical** in Dart and native code (case-sensitive).
454-
* On Android, ensure the channel is registered inside `configureFlutterEngine()`.
455-
* On iOS, set up the `FlutterMethodChannel` inside `didFinishLaunchingWithOptions()`.
456-
* Log the available channels/methods to confirm registration during app startup.
457-
* Native channel implementations require a full restart because the platform-specific code must be recompiled and relinked.
457+
* Confirm that the channel name is **identical** in Dart and native code (case-sensitive).
458+
* On Android, ensure the channel is registered inside `configureFlutterEngine()`.
459+
* On iOS, set up the `FlutterMethodChannel` inside `didFinishLaunchingWithOptions()`.
460+
* Log the available channels/methods to confirm registration during app startup.
461+
* Native channel implementations require a full restart because the platform-specific code must be recompiled and relinked.
458462
459-
---
460463
461-
###
462464
463-
- **Incorrect Argument or Result Types** {#2.-incorrect-argument-or-result-types}
465+
### Incorrect Argument or Result Types
464466
465-
**Symptom:** App crashes with type casting errors or returns `null` unexpectedly.
467+
**Symptom:** App crashes with type casting errors or returns `null` unexpectedly.
466468
467-
**What it means:** The data passed between Dart and native does not match expected formats.
469+
**What it means:** The data passed between Dart and native does not match expected formats.
468470
469-
**Common causes:**
471+
**Common causes:**
470472
471-
* Dart sends an argument as a Map but native expects a String, or vice versa.
472-
* Native code returns a platform object that can't be serialized by Flutter.
473-
* The return value is not compatible with `StandardMessageCodec`.
473+
* Dart sends an argument as a Map but native expects a String, or vice versa.
474+
* Native code returns a platform object that can't be serialized by Flutter.
475+
* The return value is not compatible with `StandardMessageCodec`.
474476
475-
**How to fix:**
477+
**How to fix:**
476478
477-
* Only use standard types: `int`, `double`, `String`, `bool`, `List`, or `Map` with JSON-safe contents.
478-
* On Dart side, specify the expected return type with generics: `invokeMethod<int>(...)`.
479-
* On native side, validate input types before using them. Consider using try/catch or safe casting.
480-
* Avoid sending complex objects like native SDK responses directly—convert to a simple dictionary or string.
479+
* Only use standard types: `int`, `double`, `String`, `bool`, `List`, or `Map` with JSON-safe contents.
480+
* On Dart side, specify the expected return type with generics: `invokeMethod<int>(...)`.
481+
* On native side, validate input types before using them. Consider using try/catch or safe casting.
482+
* Avoid sending complex objects like native SDK responses directly—convert to a simple dictionary or string.
481483
482-
---
483484
484-
### **3\. No Response or App Hangs** {#3.-no-response-or-app-hangs}
485+
### No Response or App Hangs
485486
486487
**Symptom:** The Dart call to `invokeMethod()` never returns, or the UI freezes.
487488
@@ -500,9 +501,7 @@ MethodChannels are powerful but require careful implementation. When the Dart an
500501
* Offload slow operations to a background thread or coroutine (Kotlin) or dispatch queue (Swift).
501502
* Use Dart timeouts or loading indicators to keep the UI responsive while waiting.
502503
503-
---
504-
505-
### **4\. Calling `result` Multiple Times** {#4.-calling-result-multiple-times}
504+
### Calling `result` Multiple Times
506505
507506
**Symptom:** The app crashes with a runtime error like "Reply already submitted" or shows inconsistent results.
508507
@@ -520,42 +519,40 @@ MethodChannels are powerful but require careful implementation. When the Dart an
520519
* Use return statements or guards to prevent multiple result calls.
521520
* Structure async callbacks carefully to ensure only one callback path runs.
522521
523-
---
524522
525-
### **5\. Debugging Tips by Platform** {#5.-debugging-tips-by-platform}
523+
### Debugging Tips by Platform
526524
527-
#### **Flutter/Dart:** {#flutter/dart:}
525+
**Flutter/Dart:**
528526
529527
* Use `print()` or `debugPrint()` to log method calls and results.
530528
* Always wrap `invokeMethod` in `try/catch` and log exceptions.
531529
* Add logs before and after `invokeMethod()` to verify flow.
532530
* Use Flutter DevTools to inspect console logs and application state.
533531
534-
#### **Android (Kotlin/Java):** {#android-(kotlin/java):}
532+
**Android (Kotlin/Java):**
535533
536534
* Use `Log.d("MethodChannel", "Received: ${call.method}")` inside the handler.
537535
* Use `adb logcat | grep flutter` to filter platform logs.
538536
* Ensure `configureFlutterEngine()` is actually called—older project setups may require manual configuration.
539537
* Use breakpoints in Android Studio for step-by-step inspection.
540538
541-
#### **iOS (Swift/Objective-C):** {#ios-(swift/objective-c):}
539+
**iOS (Swift/Objective-C):**
542540
543541
* Use `print()` or `NSLog()` to trace handler execution.
544542
* Watch the Xcode console for startup logs or channel registration issues.
545543
* Ensure you're calling `result(...)` correctly and only once.
546544
* Check if the `AppDelegate` is properly casting `window?.rootViewController` to `FlutterViewController`.
547545
548-
---
549546
550547
By understanding and anticipating these pitfalls, developers can avoid common errors that derail Flutter-to-native communication. MethodChannels are extremely reliable when implemented correctly, and with structured debugging, most issues can be diagnosed and resolved quickly—even in FlutterFlow-generated apps where visibility into the build system may be limited.
551548
552-
## **Performance and Architecture Best Practices** {#performance-and-architecture-best-practices}
549+
## Performance and Architecture Best Practices
553550
554551
Integrating native functionality through MethodChannels can bring significant value to your app \- but only if it’s done with performance and maintainability in mind. Below are the five most important best practices engineers should apply in real-world production apps, along with deeper insights into why each one matters.
555552
556553
**Important Context for FlutterFlow Users:** FlutterFlow generates clean Dart code and supports Custom Actions for inserting Dart logic, but it does not currently support inline native (Kotlin/Swift) editing.
557554
558-
### **1\. Don’t Block the Main Thread** {#1.-don’t-block-the-main-thread}
555+
### Don’t Block the Main Thread
559556
560557
* By default, all MethodChannel calls are handled on the **main UI thread**, which is also responsible for rendering the app.
561558
* Native operations like database access, file I/O, Bluetooth scanning, or network requests **must** be moved off the main thread.
@@ -566,7 +563,7 @@ Blocking the UI thread for even a few milliseconds can cause dropped frames, jan
566563
567564
**FlutterFlow Tip:** While UI interactions and workflows look smooth inside FlutterFlow, once you export and test the app on a real device, slow operations in Kotlin or Swift can still freeze the app. Always delegate those tasks to background threads before calling back into Dart.
568565
569-
### **2\. Keep MethodChannel Code Minimal** {#2.-keep-methodchannel-code-minimal}
566+
### Keep MethodChannel Code Minimal
570567
571568
* Your MethodChannel handler should act like a **controller**, not a service. It should delegate execution to well-structured, modular native components.
572569
* This keeps the interface between Dart and native thin and easy to maintain.
@@ -575,9 +572,9 @@ Blocking the UI thread for even a few milliseconds can cause dropped frames, jan
575572
576573
Clean separation of concerns leads to better test coverage, easier onboarding, and avoids hard-to-debug cross-layer bugs.
577574
578-
###
579575
580-
### **3\. Use Only JSON-Compatible Data** {#3.-use-only-json-compatible-data}
576+
577+
### Use Only JSON-Compatible Data
581578
582579
* The Flutter engine uses `StandardMessageCodec` for MethodChannel communication.
583580
* It supports only a limited set of Dart-native types: `int`, `double`, `bool`, `String`, `List`, `Map`, and `null`.
@@ -588,13 +585,13 @@ Type mismatches across the bridge don’t fail at compile time—they crash at r
588585
589586
**FlutterFlow Tip:** When using Dart Custom Actions that invoke MethodChannels, ensure the return values can be used in FlutterFlow bindings. Only supported types (like `String` or `int`) can be stored in App State or used in conditions or widgets.
590587
591-
### **4\. Validate and Sanitize Dart Inputs** {#4.-validate-and-sanitize-dart-inputs}
588+
### Validate and Sanitize Dart Inputs
592589
593590
* Treat incoming Dart method calls like external API requests. Assume they can be malformed.
594591
* Use pattern matching (switch/case or `when`) to route and verify each method call.
595592
* Validate presence and type of arguments before using them. For example:
596593
597-
```
594+
```js
598595
val timeout = call.argument<Int>("timeout") ?: return result.error("INVALID", "Missing timeout", null)
599596
```
600597
@@ -604,7 +601,7 @@ Dart developers might call your method incorrectly. Native code must fail safely
604601
605602
**FlutterFlow Tip:** Custom Actions in FlutterFlow can include parameters from the UI, but if the parameter isn’t set or passed correctly in a workflow, the Dart code will still execute. Validate these inputs natively before use.
606603
607-
### **5\. Log Clearly on Both Sides** {#5.-log-clearly-on-both-sides}
604+
### Log Clearly on Both Sides
608605
609606
* Add logging on both Dart and native layers for every MethodChannel call:
610607
* What method was called?
@@ -617,11 +614,10 @@ When something goes wrong in production, good logs make the difference between a
617614
618615
**FlutterFlow Tip:** Use `debugPrint()` inside Dart Custom Actions to log output alongside platform logs. In test builds, these logs help verify whether native results are arriving as expected.
619616
620-
---
621617
622618
By applying these best practices, you can ensure your MethodChannel integrations remain robust, secure, and maintainable across app updates and platform development.
623619
624-
## **Summary & Guidance** {#summary-&-guidance}
620+
## Summary & Guidance
625621
626622
MethodChannels are a foundational tool for extending the power of your app beyond what plugins alone can provide. They allow direct access to platform-native APIs and SDKs, enabling teams to solve tough integration challenges and deliver production-grade features with full control.
627623

0 commit comments

Comments
 (0)