Common native functions for flutter
Handles common native tasks for Android and IOS.
All permissions related tasks like camera,notifications are handled for you so you only need to focus on the tasks at hand
final _platform = SabianNativeCommon();
void _takePicture(BuildContext context) {
_platform.media.takePicture().then((payload) {
_setCurrentImage(payload.images!.first);
print("Success");
}).onError((error, stackTrace) {
print("Error $error");
});
}
void _choosePicture(BuildContext context) {
final config = PhotoData()
..galleryAlbumName = "Flutter Album"
..galleryToolBarTitle = "Choose Photo"
..galleryAlbumsTitle = "All Flutter Albums"
..cameraTitle = "Take Picture"
..allowEditing = true
..galleryMaximumPhotos = 3;
_platform.media.choosePicture(config).then((payload) {
_setCurrentImage(payload.images!.first);
print("Success");
}).onError((error, stackTrace) {
print("Error $error");
});
}
void _notify(BuildContext context) {
final config = NotificationData()
..title = "Flutter Notification"
..message = "This is a message from the flutter desk. Watch yourself"
..canVibrate = true
..hasSound = true;
_platform.notification.notify(config).then((payload) {
print("Success");
}).onError((error, stackTrace) {
print("Error $error");
});
}
void _getPlatform() {
_platform.device.getPlatformVersion().then((value) {
setState(() {
_platformName = "Platform : ${value ?? "Unknown"}";
});
}).onError((error, stackTrace) {
setState(() {
_platformName = "Unknown";
});
});
}
void _setCurrentImage(Uint8List bytes) {
setState(() {
memoryImage = MemoryImage(bytes);
});
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sabian_native_common/sabian_native_common.dart';
import 'package:sabian_native_common/structures/notification_data.dart';
import 'package:sabian_native_common/structures/photo_data.dart';
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final _platform = SabianNativeCommon();
ImageProvider<Object>? _memoryImage;
final ImageProvider<Object> _assetImage =
const AssetImage("assets/images/cat2.jpeg");
String? _platformName;
StreamSubscription? _progressSubscription;
String? _loadText;
@override
void initState() {
super.initState();
_registerSubs();
}
@override
void dispose() {
_progressSubscription?.cancel();
_platform.media.events.progress.unRegister();
super.dispose();
}
void _registerSubs() {
_progressSubscription =
_platform.media.events.progress.stream.listen((event) {
setState(() {
if (event.isHidden == true) {
_loadText = null;
} else {
_loadText = "${event.title ?? "Loading"} ${event.message}";
}
});
});
///Must be called for the event stream to be activated
/// Remember to call [_platform.media.events.progress.unRegister] when the widget is disposed
_platform.media.events.progress.register();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Sabian common native app'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
if (_loadText != null)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child:
CircularProgressIndicator(color: theme.primaryColor)),
const SizedBox(width: 10),
Text(_loadText!)
],
),
Container(
margin: const EdgeInsetsDirectional.symmetric(vertical: 10),
child: Text(
_platformName ?? "",
)),
SizedBox(
width: 100,
height: 100,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
child: Image(
image: _memoryImage ?? _assetImage,
fit: BoxFit.fill,
),
)),
FractionallySizedBox(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: _button(
text: "Take Picture",
textColor: theme.colorScheme.onPrimary,
backgroundColor: theme.colorScheme.primary,
fontSize: 14,
onPressed: () => _takePicture(context),
))),
FractionallySizedBox(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: _button(
text: "Choose Picture",
textColor: theme.colorScheme.onPrimary,
backgroundColor: theme.colorScheme.primary,
fontSize: 14,
onPressed: () => _choosePicture(context),
))),
FractionallySizedBox(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: _button(
text: "Notify",
textColor: theme.colorScheme.onPrimary,
backgroundColor: theme.colorScheme.primary,
fontSize: 14,
onPressed: () => _notify(context),
))),
FractionallySizedBox(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: _button(
text: "Get Platform",
textColor: theme.colorScheme.onPrimary,
backgroundColor: theme.colorScheme.primary,
fontSize: 14,
onPressed: () => _getPlatform(),
))),
],
),
),
);
}
void _takePicture(BuildContext context) {
_platform.media.takePicture().then((payload) {
setCurrentImage(payload.images!.first);
});
}
void _choosePicture(BuildContext context) {
final config = PhotoData()
..galleryAlbumName = "Flutter Album"
..galleryToolBarTitle = "Choose Photo"
..galleryAlbumsTitle = "All Flutter Albums"
..cameraTitle = "Take Picture"
..allowEditing = true
..canProcessPermissions = true
..galleryMaximumPhotos = 5;
_platform.media.choosePicture(config).then((payload) {
setCurrentImage(payload.images!.first);
});
}
void _notify(BuildContext context) {
final config = NotificationData()
..title = "Flutter Notification"
..message = "This is a message from the flutter desk. Watch yourself"
..canVibrate = true
..canProcessPermissions = true
..hasSound = true;
_platform.notification.notify(config);
}
void _getPlatform() {
_platform.device.getPlatformVersion().then((value) {
setState(() {
_platformName = "Platform : ${value ?? "Unknown"}";
});
}).onError((error, stackTrace) {
setState(() {
_platformName = "Unknown";
});
});
}
void setCurrentImage(Uint8List bytes) {
setState(() {
_memoryImage = MemoryImage(bytes);
});
}
Widget _button({required String text,
required Function()? onPressed,
Color? textColor,
Color? backgroundColor,
double? fontSize,
FontWeight? fontWeight,
BorderRadius? borderRadius,
Color? pressedBackgroundColor}) {
return TextButton(
onPressed: onPressed,
style: ButtonStyle(
shape: (borderRadius == null)
? null
: MaterialStateProperty.resolveWith((states) {
return RoundedRectangleBorder(borderRadius: borderRadius);
}),
backgroundColor: MaterialStateProperty.resolveWith((states) {
if (states.contains(MaterialState.pressed)) {
return pressedBackgroundColor ?? backgroundColor;
}
return backgroundColor;
})),
child: Text(
text,
softWrap: true,
style: TextStyle(
decoration: TextDecoration.none,
color: textColor,
backgroundColor: null,
fontSize: fontSize,
fontWeight: fontWeight,
),
));
}
}
Please see example folder for more usage examples
Add the dependency as shown
sabian_native_common:
path: ../
Under the android/app/src/main open AndroidManifest.xml and add the following that will be used for the necessary permissions for any native related tasks
Remember to only include the permissions you need
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<application android:name="${applicationName}" android:icon="@mipmap/ic_launcher"
android:label="sabian_native_common_example">
<!--...... the rest goes here -->
</application>
If you don't need to use picture or any media related activity, ignore the following 2 instructions.
Otherwise, under the android/app/src/main/res folder create 'xml' folder. Under which, create provider_paths.xml and add the following:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="." />
</paths>
Afterwards, under the android/app/src/main, open AndroidManifest.xml and add the following provider path xml code under the tag as shown
<application android:name="${applicationName}" android:icon="@mipmap/ic_launcher"
android:label="sabian_native_common_example">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<!--...... the rest goes here -->
</application>
If you don't need to use picture or any media related activity, ignore this.
Otherwise, before editing the main activity, build the project.
After building, under the android/app/src/main/kotlin/your_package_directory folder, open MainActivity and extend MediaActivity as shown
package com.sabiantech.sabian_native_common_example
import com.sabiantech.sabian_native_common.utilities.media.MediaActivity
class MainActivity : MediaActivity()
Build/Run the flutter project. You are all set for Android
In case you can't build the Android project due to this error 'plugin with id sabian_native_common not found', open the android/settings.gradle, add the following script to manually include all project android based plugins in your project:
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
Build/run the Flutter project again
If you wish the plug in to handle permissions for you, do the following. Otherwise, ignore the following instructions:
Under the ios folder, open the Podfile and add the following permission handlers under target project
Remember to only include the permissions you need otherwise your app will be flagged by Apple and you may need to explain the need of all those permissions. For a list of all supported permissions, see https://cocoapods.org/pods/SPPermissions
pod 'SPPermissions/Camera'
pod 'SPPermissions/PhotoLibrary'
pod 'SPPermissions/Notification'
e.g
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
pod 'SPPermissions/Camera'
pod 'SPPermissions/PhotoLibrary'
pod 'SPPermissions/Notification'
#Add all permissions related dependancies here using SPPermissions/
#More see https://cocoapods.org/pods/SPPermissions
target 'RunnerTests' do
inherit! :search_paths
end
end
#rest goes here
Under the sabian_native_common_ios/ios directory, open Classes folder and click SabianNativePermissionsList.swift and uncomment the permissions you need based on the specified permissions above e.g
import Foundation
import SPPermissions
class SabianNativePermissionsList {
static var permissions : Dictionary<String,SPPermissions.Permission> {
var permissions = Dictionary<String,SPPermissions.Permission>()
//permissions["Camera"] = SPPermissions.Permission.camera
//permissions["Notification"] = SPPermissions.Permission.notification
//permissions["Photo Library"] = SPPermissions.Permission.photoLibrary
return permissions
}
}
Current supported permissions are
- Camera
- Notification
- PhotoLibrary
Last but not the least, developing for IOS requires you give a description of how you intend to use certain permissions in your application In order to use the needed/required permissions, do the following
Open Xcode and edit the info.plist file inside the ios folder and add the following keys with their values. The value will correspond to the description on how you intend to use the corresponding permission:
Privacy - Camera Usage Description
Privacy - Photo Library Usage Description
Privacy - Media Library Usage Description
Alternatively, you can edit the info.plist file directly and add the permissions as shown
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) We use camera images to personalize your profile details. These details are not shared with any other party</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) uses this feature to further personalize your profile. These details are not shared with any other party</string>
<key>NSAppleMusicUsageDescription</key>
<string>$(PRODUCT_NAME) uses this feature to further personalize your profile. These details are not shared with any other party</string>
Close xcode and build/run the flutter project
You're all set for IOS
Current supported tasks are
- Device
- Get Platform Version
- Media
- Take Picture
- Choose from Gallery
- Notification
- Post notifications
More coming soon
- Android Image Picker (Android) : https://github.com/esafirm/android-image-picker
- Dexter Permissions (Android) : https://github.com/Karumi/Dexter
- SPPermissions (IOS) : https://cocoapods.org/pods/SPPermissions
- YPImagePicker (IOS) : https://github.com/Yummypets/YPImagePicker