Obafemi Emmanuel

Flutter Advanced Concepts

Published 3 months ago

Flutter is a powerful framework for building cross-platform applications, but mastering advanced concepts is key to delivering high-performance and feature-rich apps. In this blog, we’ll dive into five crucial Flutter topics: background services, push notifications, WebSockets, permissions handling, and platform-specific code.


1. Background Services & WorkManager

Background services allow apps to execute tasks even when they are not in the foreground. In Flutter, the most common way to run background tasks is using the WorkManager package.


Implementing WorkManager in Flutter

Add Dependenciesdependencies:
  workmanager: ^0.5.1
  1. Initialize WorkManager in main.dart:
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    print("Executing background task: $task");
    return Future.value(true);
  });
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Workmanager().initialize(callbackDispatcher, isInDebugMode: true);
}
  1. Register Background Tasks:
Workmanager().registerOneOffTask("uniqueTask", "backgroundTask");

This setup ensures tasks are scheduled even when the app is closed.


2. Push Notifications with Firebase

Push notifications keep users engaged. Flutter supports push notifications using Firebase Cloud Messaging (FCM).


Steps to Integrate Firebase Push Notifications:

  1. Add Firebase to Your Flutter App
  • Visit the Firebase Console
  • Register your app and download google-services.json (Android) or GoogleService-Info.plist (iOS)
  1. Add Dependencies
dependencies:
  firebase_core: ^latest_version
  firebase_messaging: ^latest_version
  1. Initialize Firebase in main.dart:
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}
  1. Handle Notifications:
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print("Message received: ${message.notification?.title}");
});
  1. Request Permissions (iOS only):
FirebaseMessaging.instance.requestPermission();

3. Using Flutter WebSockets

WebSockets allow real-time bidirectional communication between the app and a server. Flutter provides WebSocket support using the web_socket_channel package.


Steps to Use WebSockets:

  1. Add Dependency
dependencies:
  web_socket_channel: ^latest_version
  1. Connect to a WebSocket Server:
final channel = WebSocketChannel.connect(
  Uri.parse("wss://example.com/socket"),
);
  1. Send & Receive Data:
channel.sink.add("Hello, WebSocket!");

channel.stream.listen((message) {
  print("New message: $message");
});
  1. Close the Connection:
channel.sink.close();

4. Handling Permissions in Flutter

Some features like location, camera, or storage require explicit permissions. The permission_handler package helps in managing app permissions.


Steps to Handle Permissions:

  1. Add Dependency
dependencies:
  permission_handler: ^latest_version
  1. Request Permissions:
Permission.camera.request().then((status) {
  if (status.isGranted) {
    print("Camera access granted");
  }
});
  1. Check Permission Status:
if (await Permission.location.isGranted) {
  print("Location permission granted");
}
  1. Modify AndroidManifest.xml and Info.plist
  • For Android, add required permissions inside AndroidManifest.xml
  • For iOS, update Info.plist with corresponding usage descriptions

5. Platform-Specific Code in Flutter

While Flutter provides a unified API, some functionalities require platform-specific implementations using platform channels.


Writing Native Code in Flutter

  1. Define a Method Channel in Dart:
const platform = MethodChannel("com.example.native");

Future<String> getBatteryLevel() async {
  final level = await platform.invokeMethod("getBatteryLevel");
  return "Battery Level: $level%";
}
  1. Handle Calls in Android (Kotlin):
class MainActivity : FlutterActivity() {
    private val CHANNEL = "com.example.native"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                if (call.method == "getBatteryLevel") {
                    val batteryLevel = getBatteryLevel()
                    result.success(batteryLevel)
                }
            }
    }
}
  1. Handle Calls in iOS (Swift):
class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller = window?.rootViewController as! FlutterViewController
        let batteryChannel = FlutterMethodChannel(name: "com.example.native", binaryMessenger: controller.binaryMessenger)
        batteryChannel.setMethodCallHandler { (call, result) in
            if call.method == "getBatteryLevel" {
                result(UIDevice.current.batteryLevel * 100)
            }
        }
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

Conclusion

Mastering these advanced Flutter concepts will help you build robust, high-performance applications that can run seamlessly across platforms. Whether it's handling background tasks, integrating push notifications, leveraging WebSockets, managing permissions, or writing platform-specific code, Flutter provides the flexibility to achieve it all.

By implementing these techniques, you’ll be well on your way to developing scalable and production-ready Flutter applications!


Leave a Comment


Choose Colour