1
0
mirror of https://github.com/flutter/samples.git synced 2025-11-12 07:48:55 +00:00

[federated_plugin] adds platform interface implementation and plugin implementation for Android (#503)

This commit is contained in:
Ayush Bherwani
2020-07-29 09:17:36 +05:30
committed by GitHub
parent 3bc860f5df
commit 6d909874db
19 changed files with 622 additions and 101 deletions

View File

@@ -40,4 +40,5 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.android.gms:play-services-location:17.0.0'
}

View File

@@ -1,3 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.flutter.federated_plugin">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

View File

@@ -4,37 +4,109 @@
package dev.flutter.federated_plugin
import androidx.annotation.NonNull;
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import androidx.annotation.NonNull
import androidx.core.app.ActivityCompat.requestPermissions
import androidx.core.content.ContextCompat
import com.google.android.gms.location.LocationServices.getFusedLocationProviderClient
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import io.flutter.plugin.common.PluginRegistry
/** FederatedPlugin */
public class FederatedPlugin: FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "federated_plugin")
channel.setMethodCallHandler(this);
}
class FederatedPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.RequestPermissionsResultListener {
private lateinit var channel: MethodChannel
private lateinit var context: Context
private lateinit var activity: Activity
private lateinit var result: Result
private val REQUEST_CODE = 1001
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "location")
channel.setMethodCallHandler(this)
context = flutterPluginBinding.applicationContext
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
this.result = result
if (call.method == "getLocation") {
// Check for the runtime permission if SDK version is greater than 23. If permissions
// are granted, send the location data back to Dart. If permissions are not granted,
// request for the runtime permissions.
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP && !checkPermissions()) {
requestPermissions(activity, arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
), REQUEST_CODE)
} else {
provideLocation()
}
} else {
result.notImplemented()
}
}
// Method to fetch and send the last known location of the device to Dart.
private fun provideLocation() {
getFusedLocationProviderClient(context).lastLocation
.addOnSuccessListener { location ->
if (location != null) {
result.success(listOf(location.longitude, location.latitude))
} else {
result.error("NOT_DETERMINED", "Not able to determine location", null)
}
}.addOnFailureListener { exception ->
result.error("Error", exception.message, null)
}
}
// Method to check permissions to access the location data.
private fun checkPermissions(): Boolean {
val fineLocationPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
val coarseLocationPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
return fineLocationPermission == PackageManager.PERMISSION_GRANTED &&
coarseLocationPermission == PackageManager.PERMISSION_GRANTED
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onDetachedFromActivity() {}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
binding.addRequestPermissionsResultListener(this)
}
override fun onDetachedFromActivityForConfigChanges() {}
// Callback for the result after requesting for runtime permissions. If permissions
// are granted, send the location data, or send an error back to Dart if permissions
// are not granted.
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>?, grantResults: IntArray): Boolean {
if (requestCode == REQUEST_CODE && grantResults.isNotEmpty()) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
provideLocation()
} else {
result.error("PERMISSION_DENIED", "Permission denied from User", null)
}
}
return false
}
}

View File

@@ -2,60 +2,74 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:federated_plugin/federated_plugin.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await FederatedPlugin.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Text('Running on: $_platformVersion\n'),
),
home: HomePage(),
);
}
}
/// Demonstrates how to use the getLocation method from federated_plugin to access
/// location data.
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Location location;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Federated Plugin Demo'),
),
body: Builder(
builder: (context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
location == null
? SizedBox.shrink()
: Text(
'Latitude: ${location.latitude}\n'
'Longitude: ${location.longitude}',
style: Theme.of(context).textTheme.headline5,
),
SizedBox(height: 16),
RaisedButton(
child: Text('Get Location'),
onPressed: () async {
try {
final result = await getLocation();
setState(() {
location = result;
});
} catch (error) {
Scaffold.of(context).showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).primaryColor,
content: Text(error.message as String),
),
);
}
},
),
],
),
);
},
),
);
}

View File

@@ -64,6 +64,13 @@ packages:
relative: true
source: path
version: "0.0.1"
federated_plugin_platform_interface:
dependency: transitive
description:
path: "../../federated_plugin_platform_interface"
relative: true
source: path
version: "0.0.1"
flutter:
dependency: "direct main"
description: flutter
@@ -102,6 +109,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
sky_engine:
dependency: transitive
description: flutter

View File

@@ -2,23 +2,35 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:federated_plugin/federated_plugin.dart';
import 'package:federated_plugin_example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:federated_plugin_example/main.dart';
void main() {
testWidgets('Verify Platform version', (tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
group('federated plugin demo tests', () {
final location = Location(latitude: 131.0, longitude: 221.0);
setUpAll(() {
MethodChannel('location').setMockMethodCallHandler((call) async {
if (call.method == 'getLocation') {
return [location.longitude, location.latitude];
}
});
});
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(widget) => widget is Text &&
widget.data.startsWith('Running on:'),
),
findsOneWidget,
);
testWidgets('get location from platform', (tester) async {
await tester.pumpWidget(MyApp());
// Tap button to get location from platform.
await tester.tap(find.byType(RaisedButton));
await tester.pumpAndSettle();
expect(
find.text('Latitude: ${location.latitude}\n'
'Longitude: ${location.longitude}'),
findsOneWidget,
);
});
});
}

View File

@@ -4,13 +4,13 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:federated_plugin_platform_interface/federated_plugin_platform_interface.dart';
import 'package:federated_plugin_platform_interface/location_model.dart';
export 'package:federated_plugin_platform_interface/location_model.dart';
class FederatedPlugin {
static const MethodChannel _channel = MethodChannel('federated_plugin');
static Future<String> get platformVersion async {
final version = await _channel.invokeMethod<String>('getPlatformVersion');
return version;
}
/// Returns [Location] to provide latitude and longitude.
///
/// It uses [FederatedPluginInterface] interface to provide location.
Future<Location> getLocation() async {
return await FederatedPluginInterface.instance.getLocation();
}

View File

@@ -50,6 +50,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
federated_plugin_platform_interface:
dependency: "direct main"
description:
path: "../federated_plugin_platform_interface"
relative: true
source: path
version: "0.0.1"
flutter:
dependency: "direct main"
description: flutter
@@ -88,6 +95,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.2"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
sky_engine:
dependency: transitive
description: flutter

View File

@@ -12,6 +12,8 @@ environment:
dependencies:
flutter:
sdk: flutter
federated_plugin_platform_interface:
path: ../federated_plugin_platform_interface
dev_dependencies:
flutter_test:

View File

@@ -7,21 +7,20 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:federated_plugin/federated_plugin.dart';
void main() {
const channel = MethodChannel('federated_plugin');
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
channel.setMockMethodCallHandler((methodCall) async {
return '42';
group('Federated Plugin Test', () {
final location = Location(latitude: 131.0, longitude: 221.0);
MethodChannel('location').setMockMethodCallHandler((call) async {
if (call.method == 'getLocation') {
return [location.longitude, location.latitude];
}
});
test('getLocation method test', () async {
final result = await getLocation();
expect(result.longitude, location.longitude);
expect(result.latitude, location.latitude);
});
});
tearDown(() {
channel.setMockMethodCallHandler(null);
});
test('getPlatformVersion', () async {
expect(await FederatedPlugin.platformVersion, '42');
});
}