Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[platform_channels] adds EventChannel Demo #462

Merged
merged 12 commits into from
Jun 12, 2020
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package dev.flutter.platform_channels

import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import io.flutter.plugin.common.EventChannel

class AccelerometerStreamHandler(sManager: SensorManager, s: Sensor) : EventChannel.StreamHandler, SensorEventListener {
private val sensorManager: SensorManager = sManager
private val accelerometerSensor: Sensor = s
private lateinit var eventSink: EventChannel.EventSink

override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
if (events != null) {
eventSink = events
sensorManager.registerListener(this, accelerometerSensor, SensorManager.SENSOR_DELAY_UI)
}
}

override fun onCancel(arguments: Any?) {
sensorManager.unregisterListener(this)
}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}

override fun onSensorChanged(sensorEvent: SensorEvent?) {
if (sensorEvent != null) {
val axisValues = listOf(sensorEvent.values[0], sensorEvent.values[1], sensorEvent.values[2])
eventSink.success(axisValues)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package dev.flutter.platform_channels

import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
Expand All @@ -27,5 +35,10 @@ class MainActivity : FlutterActivity() {
}
}
}

val sensorManger: SensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val accelerometerSensor: Sensor = sensorManger.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
EventChannel(flutterEngine.dartExecutor, "eventChannelDemo")
.setStreamHandler(AccelerometerStreamHandler(sensorManger, accelerometerSensor))
}
}
8 changes: 7 additions & 1 deletion platform_channels/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:platform_channels/src/event_channel_demo.dart';
import 'package:platform_channels/src/method_channel_demo.dart';

void main() {
Expand All @@ -15,6 +16,7 @@ class PlatformChannelSample extends StatelessWidget {
return MaterialApp(
routes: {
'/methodChannelDemo': (context) => MethodChannelDemo(),
'/eventChannelDemo': (context) => EventChannelDemo(),
},
title: 'Platform Channel Sample',
home: HomePage(),
Expand All @@ -33,9 +35,13 @@ class DemoInfo {

List<DemoInfo> demoList = [
DemoInfo(
'MethodChannelDemo',
'MethodChannel Demo',
'/methodChannelDemo',
),
DemoInfo(
'EventChannel Demo',
'/eventChannelDemo',
)
];

class HomePage extends StatelessWidget {
Expand Down
37 changes: 37 additions & 0 deletions platform_channels/lib/src/accelerometer_event_channel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/services.dart';

/// This class includes the implementation for [EventChannel] to listen to value
/// changes from the Accelerometer sensor from native side. It has a [readings]
/// getter to provide a stream of [AccelerometerReadings].
class Accelerometer {
static final _eventChannel = const EventChannel('eventChannelDemo');

/// Method responsible for providing a stream of [AccelerometerReadings] to listen
/// to value changes from the Accelerometer sensor.
static Stream<AccelerometerReadings> get readings {
return _eventChannel.receiveBroadcastStream().map(
(dynamic event) => AccelerometerReadings(
event[0] as double,
event[1] as double,
event[2] as double,
),
);
}
}

class AccelerometerReadings {
/// Acceleration force along the x-axis.
final double x;

/// Acceleration force along the y-axis.
final double y;

/// Acceleration force along the z-axis.
final double z;

AccelerometerReadings(this.x, this.y, this.z);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional suggestion, but you could make this a const constructor.

}
58 changes: 58 additions & 0 deletions platform_channels/lib/src/event_channel_demo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:platform_channels/src/accelerometer_event_channel.dart';

/// Demonstrates how to use [EventChannel] to listen continuous values
/// of Accelerometer Sensor from platform.
///
/// The widget uses a [StreamBuilder] to rebuild it's descendant whenever it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, "the widget" is fine. It's just for the initial sentence that the noun is typically dropped.

/// listens a new value from the [Accelerometer.readings] stream. It has three
/// [Text] widgets to display the value of [AccelerometerReadings.x],
/// [AccelerometerReadings.y], and [AccelerometerReadings.z] respectively.
class EventChannelDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textStyle = Theme.of(context).textTheme.headline5;
return Scaffold(
appBar: AppBar(
title: Text('EventChannel Demo'),
),
body: Center(
child: StreamBuilder<AccelerometerReadings>(
stream: Accelerometer.readings,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'x axis: ' + snapshot.data.x.toStringAsFixed(3),
style: textStyle,
),
Text(
'y axis: ' + snapshot.data.y.toStringAsFixed(3),
style: textStyle,
),
Text(
'z axis: ' + snapshot.data.z.toStringAsFixed(3),
style: textStyle,
)
],
);
}

return Text(
'No Data Available',
style: textStyle,
);
},
),
),
);
}
}
73 changes: 73 additions & 0 deletions platform_channels/test/src/event_channel_demo_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:platform_channels/src/event_channel_demo.dart';

void main() {
group('EventChannel Demo tests', () {
final sensorValues = [1.3556, 2.3, -0.12];
setUpAll(() {
// By default EventChannel uses StandardMethodCodec to communicate with
// platform.
const standardMethod = StandardMethodCodec();

// This function handles the incoming messages from the platform. It
// calls the BinaryMessenger.setMessageHandler registered for the EventChannel
// and add the incoming message to the StreamController used by the EventChannel
// after decoding the message with codec used by the EventChannel.
void emitValues(ByteData event) {
ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(
'eventChannelDemo',
event,
(reply) {},
);
}

// Register a mock for EventChannel. EventChannel under the hood uses
// MethodChannel to listen and cancel the created stream.
ServicesBinding.instance.defaultBinaryMessenger
.setMockMessageHandler('eventChannelDemo', (message) async {
// Decode the message into MethodCallHandler.
final methodCall = standardMethod.decodeMethodCall(message);

if (methodCall.method == 'listen') {
// Emit new sensor values.
emitValues(standardMethod.encodeSuccessEnvelope(sensorValues));
emitValues(null);
return standardMethod.encodeSuccessEnvelope(null);
} else if (methodCall.method == 'cancel') {
return standardMethod.encodeSuccessEnvelope(null);
} else {
fail('Expected listen or cancel');
}
});
});

testWidgets('EventChannel AccelerometerReadings Stream test',
(tester) async {
await tester.pumpWidget(MaterialApp(
home: EventChannelDemo(),
));

await tester.pumpAndSettle();

// Check the values of axis. The value is rounded to 3 decimal places.
expect(
find.text('x axis: ' + sensorValues[0].toStringAsFixed(3)),
findsOneWidget,
);
expect(
find.text('y axis: ' + sensorValues[1].toStringAsFixed(3)),
findsOneWidget,
);
expect(
find.text('z axis: ' + sensorValues[2].toStringAsFixed(3)),
findsOneWidget,
);
});
});
}