Majid Hajian
Majid Hajian is a passionate software developer with years of developing and architecting complex web and mobile applications. He is passionate about web platform especially flutter, IoT, PWAs, and performance.
Majid Hajian
mhadaily
Majid Hajian
mhadaily
if (Platform.isIOS) {
//
}
if (Platform.isAndroid) {
//
}
if (Platform.isLinux) {
//
}
if (Platform.isMacOS) {
//
}
if (Platform.isWindows) {
//
}
if (kIsWeb) {
//
}
mhadaily
mhadaily
mhadaily
import 'package:flutter/material.dart';
MaterialApp(
ThemeData(
name: "Majid Hajian",
location: "Oslo, Norway",
description: '''
Google Developer Expert
Passionate Software engineer,
Community Leader, Author and international Speaker
''',
main: "Head of DevRel at Invertase.io",
homepage: "https://www.majidhajian.com",
socials: {
twitter: "https://www.twitter.com/mhadaily",
github: "https://www.github.com/mhadaily"
},
author: {
Pluralsight: "www.pluralsight.com/authors/majid-hajian",
Apress: "Progressive Web App with Angular, Book",
PacktPub: "PWA development",
Udemy: "PWA development",
}
founder: "Softiware As (www.Softiware.com)"
devDependencies: {
tea: "Ginger",
mac: "10.14+",
},
community: {
MobileEraConference: "Orginizer",
FlutterVikings: "Orginizer",
FlutterDartOslo: "Orginizer",
GDGOslo: "Co-Orginizer",
DevFestNorway: "Orginizer",
...more
}));
Find me on the internet by
Head of DevRel at Invertase
mhadaily
mhadaily
Platform-specific code for the web generally uses JS interoperability or the dart:html library instead.
mhadaily
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('plus.fluttercommunity.dev/battery');
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
}
mhadaily
// MainActivity.kt
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "plus.fluttercommunity.dev/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
}
}
mhadaily
typedef flutter::MethodChannel<flutter::EncodableValue> FlMethodChannel;
typedef flutter::EventChannel<flutter::EncodableValue> FlEventChannel;
BatteryPlusWindowsPlugin::BatteryPlusWindowsPlugin(
flutter::PluginRegistrarWindows *registrar) {
auto methodChannel = std::make_unique<FlMethodChannel>(
registrar->messenger(), "dev.fluttercommunity.plus/battery",
&flutter::StandardMethodCodec::GetInstance());
methodChannel->SetMethodCallHandler([this](const auto &call, auto result) {
HandleMethodCall(call, std::move(result));
});
auto eventChannel = std::make_unique<FlEventChannel>(
registrar->messenger(), "dev.fluttercommunity.plus/charging",
&flutter::StandardMethodCodec::GetInstance());
eventChannel->SetStreamHandler(
std::make_unique<BatteryStatusStreamHandler>(registrar));
}
mhadaily
void BatteryPlusWindowsPlugin::HandleMethodCall(
const FlMethodCall &method_call, std::unique_ptr<FlMethodResult> result) {
if (method_call.method_name().compare("isInBatterySaveMode") == 0) {
SystemBattery battery;
int batteryStatus = battery.GetBatterySaveMode();
if (batteryStatus == 0 || batteryStatus == 1) {
bool isBatteryMode = batteryStatus == 1;
result->Success(flutter::EncodableValue(isBatteryMode));
} else {
result->Error(std::to_string(battery.GetError()),
battery.GetErrorString());
}
} else if (method_call.method_name().compare("getBatteryLevel") == 0) {
SystemBattery battery;
int level = battery.GetLevel();
if (level >= 0) {
result->Success(flutter::EncodableValue(level));
} else {
result->Error(std::to_string(battery.GetError()),
battery.GetErrorString());
}
} else if (method_call.method_name().compare("getBatteryState") == 0) {
SystemBattery battery;
result->Success(flutter::EncodableValue(battery.GetStatusString()));
} else {
result->NotImplemented();
}
}
mhadaily
https://api.flutter.dev/flutter/services/StandardMessageCodec-class.html
https://docs.flutter.dev/development/platform-integration/platform-channels
mhadaily
Pigeon
mhadaily
mhadaily
https://pub.dev/packages/pigeon
class Value {
int? number;
}
@HostApi()
abstract class Api2Host {
@async
Value calculate(Value value);
}
// Objc
@protocol Api2Host
-(void)calculate:(nullable Value *)input
completion:(void(^)(Value *_Nullable, FlutterError *_Nullable))completion;
@end
// ----
// Java
public interface Result<T> {
void success(T result);
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter.*/
public interface Api2Host {
void calculate(Value arg, Result<Value> result);
}
// ----
// C++
/** Generated class from Pigeon that represents a handler of messages from Flutter.*/
class Api2Host {
public:
virtual void calculate(Value value, flutter::MessageReply<Value> result) = 0;
}
mhadaily
mhadaily
Plugins
mhadaily
name: battery_plus_example
description: Demonstrates how to use the battery_plus plugin.
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
battery_plus:
dev_dependencies:
flutter_driver:
sdk: flutter
integration_test:
sdk: flutter
flutter_lints: ^1.0.4
flutter:
uses-material-design: true
https://plus.fluttercommunity.dev/
mhadaily
https://firebase.flutter.dev/
mhadaily
mhadaily
A federated plugin requires:
https://medium.com/flutter/modern-flutter-plugin-development-4c3ee015cf5a
mhadaily
flutter create --template=plugin --platforms=linux battery_plus_linux
--platforms=android, ios, web, linux, macos, windows.
flutter create --template=package battery_plus_platform_interface
mhadaily
Endorsed Plugins vs Non-endorsed
// Pubspec.yaml
flutter:
plugin:
platforms:
android:
package: dev.fluttercommunity.battery_plus
pluginClass: BatteryPlusPlugin
ios:
pluginClass: BatteryPlusPlugin
macos:
pluginClass: BatteryPlusPlugin
web:
pluginClass: BatteryPlusPlugin
fileName: battery_plus_web.dart
mhadaily
// pubspec.yaml
flutter:
plugin:
platforms:
android:
package: dev.fluttercommunity.plus.battery
pluginClass: BatteryPlusPlugin
ios:
pluginClass: FLTBatteryPlusPlugin
linux:
default_package: battery_plus_linux
macos:
default_package: battery_plus_macos
web:
default_package: battery_plus_web
windows:
default_package: battery_plus_windows
dependencies:
flutter:
sdk: flutter
meta: ^1.7.0
battery_plus_platform_interface: ^1.2.0
battery_plus_linux: ^1.1.0
battery_plus_macos: ^1.1.0
battery_plus_web: ^1.1.0
battery_plus_windows: ^1.1.0
mhadaily
// batter_plus_linux/pubspec.yaml
flutter:
plugin:
implements: battery_plus
platforms:
linux:
dartPluginClass: BatteryPlusLinux
pluginClass: none
dependencies:
flutter:
sdk: flutter
battery_plus_platform_interface: ^1.2.0
dbus: ^0.6.6
meta: ^1.7.0
mhadaily
// batter_plus_web/pubspec.yaml
flutter:
plugin:
platforms:
web:
pluginClass: BatteryPlusPlugin
fileName: battery_plus_web.dart
dependencies:
battery_plus_platform_interface: ^1.2.0
flutter_web_plugins:
sdk: flutter
flutter:
sdk: flutter
mhadaily
import 'dart:async';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'method_channel_battery_plus.dart';
import 'src/enums.dart';
abstract class BatteryPlatform extends PlatformInterface {
BatteryPlatform() : super(token: _token);
static final Object _token = Object();
static BatteryPlatform _instance = MethodChannelBattery();
static BatteryPlatform get instance => _instance;
static set instance(BatteryPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
Future<int> get batteryLevel {
throw UnimplementedError('batteryLevel() has not been implemented.');
}
Future<bool> get isInBatterySaveMode {
throw UnimplementedError('isInBatterySaveMode() has not been implemented.');
}
Future<BatteryState> get batteryState {
throw UnimplementedError('batteryState() has not been implemented.');
}
Stream<BatteryState> get onBatteryStateChanged {
throw UnimplementedError(
'get onBatteryStateChanged has not been implemented.');
}
}
mhadaily
class MethodChannelBattery extends BatteryPlatform {
@visibleForTesting
MethodChannel methodChannel =
const MethodChannel('dev.fluttercommunity.plus/battery');
@visibleForTesting
EventChannel eventChannel =
const EventChannel('dev.fluttercommunity.plus/charging');
Stream<BatteryState>? _onBatteryStateChanged;
@override
Future<int> get batteryLevel => methodChannel
.invokeMethod<int>('getBatteryLevel')
.then<int>((dynamic result) => result);
@override
Future<bool> get isInBatterySaveMode => methodChannel
.invokeMethod<bool>('isInBatterySaveMode')
.then<bool>((dynamic result) => result);
@override
Future<BatteryState> get batteryState => methodChannel
.invokeMethod<String>('getBatteryState')
.then<BatteryState>((dynamic result) => parseBatteryState(result));
@override
Stream<BatteryState> get onBatteryStateChanged {
_onBatteryStateChanged ??= eventChannel
.receiveBroadcastStream()
.map((dynamic event) => parseBatteryState(event));
return _onBatteryStateChanged!;
}
}
mhadaily
import 'dart:async';
import 'package:battery_plus_platform_interface/battery_plus_platform_interface.dart';
export 'package:battery_plus_platform_interface/battery_plus_platform_interface.dart'
show BatteryState;
class Battery {
factory Battery() {
_singleton ??= Battery._();
return _singleton!;
}
Battery._();
static Battery? _singleton;
static BatteryPlatform get _platform {
return BatteryPlatform.instance;
}
Future<int> get batteryLevel {
return _platform.batteryLevel;
}
Future<bool> get isInBatterySaveMode {
return _platform.isInBatterySaveMode;
}
Future<BatteryState> get batteryState {
return _platform.batteryState;
}
Stream<BatteryState> get onBatteryStateChanged {
return _platform.onBatteryStateChanged;
}
}
mhadaily
// battery_plus_web/lib/battery_plus_web.dart
import 'dart:async';
import 'dart:html' as html show window, BatteryManager, Navigator;
import 'package:battery_plus_platform_interface/battery_plus_platform_interface.dart';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
class BatteryPlusPlugin extends BatteryPlatform {
BatteryPlusPlugin(html.Navigator navigator)
: _getBattery = navigator.getBattery;
bool get isSupported => html.window.navigator.getBattery != null;
late final Future<dynamic> Function() _getBattery;
static void registerWith(Registrar registrar) {
BatteryPlatform.instance = BatteryPlusPlugin(html.window.navigator);
}
@override
Future<int> get batteryLevel async {
if (isSupported) {
// level is a number representing the system's battery charge level scaled to a value between 0.0 and 1.0
final batteryManager = await _getBattery() as html.BatteryManager;
final level = batteryManager.level ?? 0;
return level * 100 as int;
}
return 0;
}
}
mhadaily
Phew!
mhadaily
mhadaily
mhadaily
melos.invertase.dev
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
mhadaily
Majid Hajian
find me on internet
mhadaily
Slides and link to source code
slides.com/mhadaily
By Majid Hajian
Majid Hajian is a passionate software developer with years of developing and architecting complex web and mobile applications. He is passionate about web platform especially flutter, IoT, PWAs, and performance.